1mod io;
13
14use std::cmp;
15
16use io::Cursor;
17use lerc_core::{
18 BandSetInfo, BlobInfo, DataType, Decoded, DecodedBandSet, DecodedF64, Error, NdArrayElement,
19 PixelData, Result, Version,
20};
21use ndarray::ArrayD;
22
23const MAGIC_LERC1_PREFIX: &[u8; 9] = b"CntZImage";
24const MAGIC_LERC2: &[u8; 6] = b"Lerc2 ";
25const HUFFMAN_LUT_BITS_MAX: u8 = 12;
26
27#[derive(Debug, Clone)]
28struct DepthRanges {
29 min_values: Vec<f64>,
30 max_values: Vec<f64>,
31}
32
33#[derive(Debug, Clone)]
34struct HuffmanEntry {
35 bit_len: u8,
36 value: u32,
37}
38
39#[derive(Debug, Default)]
40struct HuffmanNode {
41 value: Option<u32>,
42 left: Option<Box<HuffmanNode>>,
43 right: Option<Box<HuffmanNode>>,
44}
45
46#[derive(Debug)]
47struct HuffmanInfo {
48 quick_lut: Vec<Option<HuffmanEntry>>,
49 quick_bits: u8,
50 max_bits: u8,
51 tree: HuffmanNode,
52 stuffed_data: Vec<u32>,
53 src_ptr: usize,
54 bit_pos: u8,
55}
56
57#[derive(Debug)]
58struct HuffmanStream<'a> {
59 words: &'a [u32],
60 src_ptr: usize,
61 bit_pos: u8,
62}
63
64#[derive(Debug, Clone)]
65struct Lerc1PixelsHeader {
66 num_blocks_y: usize,
67 num_blocks_x: usize,
68 max_value: f32,
69}
70
71#[derive(Debug, Clone)]
72enum Lerc1Block {
73 Zero,
74 Constant(f32),
75 Raw(Vec<f32>),
76 Stuffed {
77 offset: f32,
78 bits_per_pixel: u8,
79 num_valid_pixels: usize,
80 stuffed_data: Vec<u32>,
81 },
82}
83
84#[derive(Debug, Clone)]
85struct Lerc1Blob {
86 info: BlobInfo,
87 mask: Option<Vec<u8>>,
88 pixels: Lerc1PixelsHeader,
89 blocks: Vec<Lerc1Block>,
90 actual_num_blocks_y: usize,
91 actual_num_blocks_x: usize,
92 base_block_height: usize,
93 base_block_width: usize,
94 eof_offset: usize,
95}
96
97fn is_lerc1(blob: &[u8]) -> bool {
98 blob.starts_with(MAGIC_LERC1_PREFIX)
99}
100
101fn is_lerc2(blob: &[u8]) -> bool {
102 blob.starts_with(MAGIC_LERC2)
103}
104
105pub fn get_blob_info(blob: &[u8]) -> Result<BlobInfo> {
106 if is_lerc1(blob) {
107 let mut parsed = parse_lerc1(blob, None)?;
108 if parsed.info.valid_pixel_count != 0 {
109 let pixels = decode_lerc1_pixels(&parsed)?;
110 let (z_min, z_max) = scan_range(&pixels, parsed.mask.as_deref())?;
111 parsed.info.z_min = z_min;
112 parsed.info.z_max = z_max;
113 }
114 return Ok(parsed.info);
115 }
116
117 let (mut info, mut cursor) = parse_lerc2(blob)?;
118 let _mask = read_mask(&mut cursor, &info, None)?;
119 if should_read_depth_ranges(&info) {
120 let ranges = read_depth_ranges(&mut cursor, &info)?;
121 info.min_values = Some(ranges.min_values);
122 info.max_values = Some(ranges.max_values);
123 }
124 Ok(info)
125}
126
127pub fn get_band_count(blob: &[u8]) -> Result<usize> {
128 let mut offset = 0usize;
129 let mut count = 0usize;
130 let mut lerc1_mask: Option<Vec<u8>> = None;
131 let mut lerc2_mask: Option<Vec<u8>> = None;
132
133 while offset < blob.len() {
134 let slice = &blob[offset..];
135 let next_len = if is_lerc1(slice) {
136 let parsed = parse_lerc1(slice, lerc1_mask.as_deref())?;
137 lerc1_mask = parsed.mask.clone();
138 lerc2_mask = None;
139 parsed.eof_offset
140 } else if is_lerc2(slice) {
141 let (info, mut cursor) = parse_lerc2(slice)?;
142 let mask = read_mask(&mut cursor, &info, lerc2_mask.as_deref())?;
143 lerc2_mask = mask;
144 lerc1_mask = None;
145 info.blob_size
146 } else {
147 return Err(Error::InvalidMagic);
148 };
149
150 let next = offset
151 .checked_add(next_len)
152 .ok_or_else(|| Error::InvalidBlob("band offset overflow".into()))?;
153 if next <= offset || next > blob.len() {
154 return Err(Error::InvalidBlob(
155 "invalid concatenated band blob size".into(),
156 ));
157 }
158
159 offset = next;
160 count += 1;
161 }
162
163 Ok(count)
164}
165
166pub fn decode(blob: &[u8]) -> Result<Decoded> {
167 decode_single(blob, None, None)
168}
169
170pub fn decode_band_set(blob: &[u8]) -> Result<DecodedBandSet> {
171 let mut offset = 0usize;
172 let mut pixels = Vec::new();
173 let mut infos = Vec::new();
174 let mut band_masks = Vec::new();
175 let mut lerc1_mask: Option<Vec<u8>> = None;
176 let mut lerc2_mask: Option<Vec<u8>> = None;
177
178 while offset < blob.len() {
179 let slice = &blob[offset..];
180 let decoded = if is_lerc1(slice) {
181 let decoded = decode_single(slice, lerc1_mask.as_deref(), None)?;
182 lerc1_mask = decoded.mask.clone();
183 lerc2_mask = None;
184 decoded
185 } else if is_lerc2(slice) {
186 let decoded = decode_single(slice, None, lerc2_mask.as_deref())?;
187 lerc2_mask = decoded.mask.clone();
188 lerc1_mask = None;
189 decoded
190 } else {
191 return Err(Error::InvalidMagic);
192 };
193
194 let next = offset
195 .checked_add(decoded.info.blob_size)
196 .ok_or_else(|| Error::InvalidBlob("band offset overflow".into()))?;
197 if next <= offset || next > blob.len() {
198 return Err(Error::InvalidBlob(
199 "invalid concatenated band blob size".into(),
200 ));
201 }
202
203 infos.push(decoded.info);
204 pixels.push(decoded.pixels);
205 band_masks.push(decoded.mask);
206 offset = next;
207 }
208
209 Ok(DecodedBandSet {
210 info: BandSetInfo::new(infos)?,
211 bands: pixels,
212 band_masks,
213 })
214}
215
216pub fn decode_band_set_ndarray<T: NdArrayElement>(blob: &[u8]) -> Result<ArrayD<T>> {
217 decode_band_set(blob)?.into_ndarray()
218}
219
220pub fn decode_band_set_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
221 decode_band_set(blob)?.into_ndarray()
222}
223
224pub fn decode_band_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
225 decode_band_set(blob)?.into_band_mask_ndarray()
226}
227
228fn decode_single(
229 blob: &[u8],
230 lerc1_shared_mask: Option<&[u8]>,
231 lerc2_shared_mask: Option<&[u8]>,
232) -> Result<Decoded> {
233 if is_lerc1(blob) {
234 let mut parsed = parse_lerc1(blob, lerc1_shared_mask)?;
235 let pixels = decode_lerc1_pixels(&parsed)?;
236 if parsed.info.valid_pixel_count != 0 {
237 let (z_min, z_max) = scan_range(&pixels, parsed.mask.as_deref())?;
238 parsed.info.z_min = z_min;
239 parsed.info.z_max = z_max;
240 }
241 return Ok(Decoded {
242 info: parsed.info,
243 pixels,
244 mask: parsed.mask,
245 });
246 }
247
248 let (info, _) = parse_lerc2(blob)?;
249 let (mut info, mut cursor) = parse_lerc2(&blob[..info.blob_size])?;
250 let mask = read_mask(&mut cursor, &info, lerc2_shared_mask)?;
251
252 let depth_ranges = if should_read_depth_ranges(&info) {
253 let ranges = read_depth_ranges(&mut cursor, &info)?;
254 info.min_values = Some(ranges.min_values.clone());
255 info.max_values = Some(ranges.max_values.clone());
256 Some(ranges)
257 } else {
258 None
259 };
260
261 let pixels = decode_pixels(&mut cursor, &info, depth_ranges.as_ref(), mask.as_deref())?;
262 Ok(Decoded { info, pixels, mask })
263}
264
265pub fn decode_to_f64(blob: &[u8]) -> Result<DecodedF64> {
266 let decoded = decode(blob)?;
267 Ok(DecodedF64 {
268 info: decoded.info,
269 pixels: decoded.pixels.to_f64(),
270 mask: decoded.mask,
271 })
272}
273
274pub fn decode_ndarray<T: NdArrayElement>(blob: &[u8]) -> Result<ArrayD<T>> {
275 decode(blob)?.into_ndarray()
276}
277
278pub fn decode_ndarray_f64(blob: &[u8]) -> Result<ArrayD<f64>> {
279 decode_to_f64(blob)?.into_ndarray()
280}
281
282pub fn decode_mask_ndarray(blob: &[u8]) -> Result<Option<ArrayD<u8>>> {
283 decode(blob)?.into_mask_ndarray()
284}
285
286fn parse_lerc1(blob: &[u8], shared_mask: Option<&[u8]>) -> Result<Lerc1Blob> {
287 let mut cursor = Cursor::new(blob);
288 let magic = cursor.read_bytes(10)?;
289 if !magic.starts_with(MAGIC_LERC1_PREFIX) {
290 return Err(Error::InvalidMagic);
291 }
292
293 let version = cursor.read_i32()?;
294 if version < 0 {
295 return Err(Error::UnsupportedVersion(version as u32));
296 }
297 let image_type = cursor.read_i32()?;
298 let height = cursor.read_u32()?;
299 let width = cursor.read_u32()?;
300 let max_z_error = cursor.read_f64()?;
301
302 let mask = if let Some(shared_mask) = shared_mask {
303 Some(shared_mask.to_vec())
304 } else {
305 read_lerc1_mask(&mut cursor, width, height)?
306 };
307
308 let pixels_num_blocks_y = cursor.read_u32()? as usize;
309 let pixels_num_blocks_x = cursor.read_u32()? as usize;
310 let _pixels_num_bytes = cursor.read_u32()? as usize;
311 let pixels_max_value = cursor.read_f32()?;
312
313 if pixels_num_blocks_x == 0 || pixels_num_blocks_y == 0 {
314 return Err(Error::InvalidHeader("Lerc1 block grid must be non-zero"));
315 }
316
317 let width_usize = width as usize;
318 let height_usize = height as usize;
319 let num_pixels = width_usize
320 .checked_mul(height_usize)
321 .ok_or_else(|| Error::InvalidBlob("pixel count overflows usize".into()))?;
322 let base_block_width = width_usize / pixels_num_blocks_x;
323 let base_block_height = height_usize / pixels_num_blocks_y;
324 let actual_num_blocks_x =
325 pixels_num_blocks_x + usize::from(width_usize % pixels_num_blocks_x != 0);
326 let actual_num_blocks_y =
327 pixels_num_blocks_y + usize::from(height_usize % pixels_num_blocks_y != 0);
328
329 let valid_pixel_count = match mask.as_deref() {
330 Some(mask) => mask.iter().map(|&value| u32::from(value != 0)).sum(),
331 None => num_pixels as u32,
332 };
333
334 let mut blocks = Vec::with_capacity(actual_num_blocks_x * actual_num_blocks_y);
335 for block_y in 0..actual_num_blocks_y {
336 let this_block_height =
337 if block_y + 1 == actual_num_blocks_y && height_usize % pixels_num_blocks_y != 0 {
338 height_usize % pixels_num_blocks_y
339 } else {
340 base_block_height
341 };
342 if this_block_height == 0 {
343 continue;
344 }
345 for block_x in 0..actual_num_blocks_x {
346 let this_block_width =
347 if block_x + 1 == actual_num_blocks_x && width_usize % pixels_num_blocks_x != 0 {
348 width_usize % pixels_num_blocks_x
349 } else {
350 base_block_width
351 };
352 if this_block_width == 0 {
353 continue;
354 }
355
356 let block_valid_pixels = if let Some(mask) = mask.as_deref() {
357 count_valid_in_block(
358 mask,
359 width_usize,
360 block_x * base_block_width,
361 block_y * base_block_height,
362 this_block_width,
363 this_block_height,
364 )
365 } else {
366 this_block_width * this_block_height
367 };
368
369 let header_byte = cursor.read_u8()?;
370 let encoding = header_byte & 63;
371 if encoding > 3 {
372 return Err(Error::InvalidBlob(format!(
373 "invalid Lerc1 block encoding {encoding}"
374 )));
375 }
376 if encoding == 2 {
377 blocks.push(Lerc1Block::Zero);
378 continue;
379 }
380
381 let offset = if header_byte != 0 && header_byte != 2 {
382 Some(read_lerc1_offset(&mut cursor, header_byte >> 6)?)
383 } else {
384 None
385 };
386
387 if encoding == 3 {
388 blocks.push(Lerc1Block::Constant(offset.ok_or(Error::InvalidBlob(
389 "Lerc1 constant block is missing its offset".into(),
390 ))?));
391 continue;
392 }
393
394 if encoding == 0 {
395 let byte_len = block_valid_pixels.checked_mul(4).ok_or_else(|| {
396 Error::InvalidBlob("Lerc1 raw block byte count overflows usize".into())
397 })?;
398 let values = cursor
399 .read_bytes(byte_len)?
400 .chunks_exact(4)
401 .map(|chunk| f32::from_le_bytes(chunk.try_into().unwrap()))
402 .collect();
403 blocks.push(Lerc1Block::Raw(values));
404 continue;
405 }
406
407 let packed_header = cursor.read_u8()?;
408 let bits_per_pixel = packed_header & 63;
409 let num_valid_pixels = match packed_header >> 6 {
410 0 => cursor.read_u32()? as usize,
411 1 => read_u16(cursor.read_bytes(2)?)? as usize,
412 2 => cursor.read_u8()? as usize,
413 other => {
414 return Err(Error::InvalidBlob(format!(
415 "invalid Lerc1 valid pixel count type {other}"
416 )))
417 }
418 };
419 let data_bytes = (num_valid_pixels * usize::from(bits_per_pixel)).div_ceil(8);
420 let stuffed_data = words_from_padded(cursor.read_bytes(data_bytes)?);
421 blocks.push(Lerc1Block::Stuffed {
422 offset: offset.ok_or(Error::InvalidBlob(
423 "Lerc1 bit-stuffed block is missing its offset".into(),
424 ))?,
425 bits_per_pixel,
426 num_valid_pixels,
427 stuffed_data,
428 });
429 }
430 }
431
432 let eof_offset = cursor.offset();
433 let info = BlobInfo {
434 version: Version::Lerc1(version as u32),
435 data_type: map_lerc1_data_type(image_type),
436 width,
437 height,
438 depth: 1,
439 min_values: None,
440 max_values: None,
441 valid_pixel_count,
442 micro_block_size: 0,
443 blob_size: eof_offset,
444 max_z_error,
445 z_min: 0.0,
446 z_max: pixels_max_value as f64,
447 };
448
449 Ok(Lerc1Blob {
450 info,
451 mask,
452 pixels: Lerc1PixelsHeader {
453 num_blocks_y: pixels_num_blocks_y,
454 num_blocks_x: pixels_num_blocks_x,
455 max_value: pixels_max_value,
456 },
457 blocks,
458 actual_num_blocks_y,
459 actual_num_blocks_x,
460 base_block_height,
461 base_block_width,
462 eof_offset,
463 })
464}
465
466fn map_lerc1_data_type(_image_type: i32) -> DataType {
467 DataType::F32
468}
469
470fn read_lerc1_offset(cursor: &mut Cursor<'_>, offset_type: u8) -> Result<f32> {
471 match offset_type {
472 0 => cursor.read_f32(),
473 1 => Ok(cursor.read_i16()? as f32),
474 2 => Ok(i8::from_le_bytes([cursor.read_u8()?]) as f32),
475 _ => Err(Error::InvalidBlob(format!(
476 "invalid Lerc1 block offset type {offset_type}"
477 ))),
478 }
479}
480
481fn read_lerc1_mask(cursor: &mut Cursor<'_>, width: u32, height: u32) -> Result<Option<Vec<u8>>> {
482 let num_blocks_y = cursor.read_u32()?;
483 let num_blocks_x = cursor.read_u32()?;
484 let num_bytes = cursor.read_u32()? as usize;
485 let max_value = cursor.read_f32()?;
486
487 let num_pixels = (width as usize)
488 .checked_mul(height as usize)
489 .ok_or_else(|| Error::InvalidBlob("pixel count overflows usize".into()))?;
490 let bitset_len = num_pixels.div_ceil(8);
491
492 if num_bytes > 0 {
493 let bitset = decode_mask_rle(cursor.read_bytes(num_bytes)?, bitset_len)?;
494 return Ok(Some(unpack_mask_bitset(&bitset, num_pixels)));
495 }
496
497 if num_blocks_y == 0 && num_blocks_x == 0 && max_value == 0.0 {
498 return Ok(Some(vec![0; num_pixels]));
499 }
500
501 Ok(None)
502}
503
504fn decode_lerc1_pixels(parsed: &Lerc1Blob) -> Result<PixelData> {
505 let width = parsed.info.width as usize;
506 let height = parsed.info.height as usize;
507 let mask = parsed.mask.as_deref();
508 let mut result = vec![0.0f32; width * height];
509 let mut block_buffer = vec![0.0f64; parsed.base_block_width * parsed.base_block_height];
510 let mut block_index = 0usize;
511
512 for block_y in 0..parsed.actual_num_blocks_y {
513 let this_block_height = if block_y + 1 == parsed.actual_num_blocks_y
514 && height % parsed.pixels.num_blocks_y != 0
515 {
516 height % parsed.pixels.num_blocks_y
517 } else {
518 parsed.base_block_height
519 };
520 if this_block_height == 0 {
521 continue;
522 }
523
524 for block_x in 0..parsed.actual_num_blocks_x {
525 let this_block_width = if block_x + 1 == parsed.actual_num_blocks_x
526 && width % parsed.pixels.num_blocks_x != 0
527 {
528 width % parsed.pixels.num_blocks_x
529 } else {
530 parsed.base_block_width
531 };
532 if this_block_width == 0 {
533 continue;
534 }
535
536 let block = &parsed.blocks[block_index];
537 block_index += 1;
538
539 let mut stuffed_values: Option<&[f64]> = None;
540 let (raw_values, constant_value) = match block {
541 Lerc1Block::Zero => (None, Some(0.0f32)),
542 Lerc1Block::Constant(value) => (None, Some(*value)),
543 Lerc1Block::Raw(values) => (Some(values.as_slice()), None),
544 Lerc1Block::Stuffed {
545 offset,
546 bits_per_pixel,
547 num_valid_pixels,
548 stuffed_data,
549 } => {
550 if *num_valid_pixels > block_buffer.len() {
551 return Err(Error::InvalidBlob(
552 "Lerc1 stuffed block expands beyond its output buffer".into(),
553 ));
554 }
555 block_buffer[..*num_valid_pixels].fill(0.0);
556 unstuff_v2(
557 stuffed_data,
558 &mut block_buffer[..*num_valid_pixels],
559 *bits_per_pixel,
560 UnstuffOptions {
561 num_pixels: *num_valid_pixels,
562 lut_values: None,
563 offset: Some(*offset as f64),
564 scale: 2.0 * parsed.info.max_z_error,
565 max_value: parsed.pixels.max_value as f64,
566 },
567 );
568 stuffed_values = Some(&block_buffer[..*num_valid_pixels]);
569 (None, None)
570 }
571 };
572
573 let mut value_index = 0usize;
574 for row in 0..this_block_height {
575 let pixel_row = block_y * parsed.base_block_height + row;
576 for col in 0..this_block_width {
577 let pixel = pixel_row * width + block_x * parsed.base_block_width + col;
578 if mask.map(|mask| mask[pixel] != 0).unwrap_or(true) {
579 let value = if let Some(value) = constant_value {
580 value
581 } else if let Some(values) = raw_values {
582 let value = values.get(value_index).copied().ok_or_else(|| {
583 Error::InvalidBlob("Lerc1 raw block payload ended early".into())
584 })?;
585 value
586 } else if let Some(values) = stuffed_values {
587 values.get(value_index).copied().ok_or_else(|| {
588 Error::InvalidBlob("Lerc1 stuffed block payload ended early".into())
589 })? as f32
590 } else {
591 unreachable!()
592 };
593 result[pixel] = value;
594 value_index += 1;
595 }
596 }
597 }
598
599 let expected_values = match block {
600 Lerc1Block::Zero | Lerc1Block::Constant(_) => 0,
601 Lerc1Block::Raw(values) => values.len(),
602 Lerc1Block::Stuffed {
603 num_valid_pixels, ..
604 } => *num_valid_pixels,
605 };
606 if expected_values != 0 && value_index != expected_values {
607 return Err(Error::InvalidBlob(
608 "Lerc1 block payload does not match the block mask".into(),
609 ));
610 }
611 }
612 }
613
614 Ok(PixelData::F32(result))
615}
616
617fn parse_lerc2(blob: &[u8]) -> Result<(BlobInfo, Cursor<'_>)> {
618 let mut cursor = Cursor::new(blob);
619 let magic = cursor.read_bytes(6)?;
620 if magic != MAGIC_LERC2 {
621 return Err(Error::InvalidMagic);
622 }
623
624 let version = cursor.read_i32()?;
625 if version < 1 {
626 return Err(Error::UnsupportedVersion(version as u32));
627 }
628 let version = version as u32;
629 if version > 6 {
630 return Err(Error::UnsupportedVersion(version));
631 }
632
633 let checksum = if version >= 3 {
634 Some(cursor.read_u32()?)
635 } else {
636 None
637 };
638
639 let height = cursor.read_u32()?;
640 let width = cursor.read_u32()?;
641 let depth = if version >= 4 { cursor.read_u32()? } else { 1 };
642
643 let valid_pixel_count = cursor.read_u32()?;
644 let micro_block_size = cursor.read_i32()?;
645 let blob_size = cursor.read_i32()?;
646 let image_type = cursor.read_i32()?;
647 let max_z_error = cursor.read_f64()?;
648 let z_min = cursor.read_f64()?;
649 let z_max = cursor.read_f64()?;
650
651 if micro_block_size < 0 {
652 return Err(Error::InvalidHeader("negative micro block size"));
653 }
654 if blob_size <= 0 {
655 return Err(Error::InvalidHeader("non-positive blob size"));
656 }
657 let blob_size = blob_size as usize;
658 if blob_size > blob.len() {
659 return Err(Error::Truncated {
660 offset: 0,
661 needed: blob_size,
662 available: blob.len(),
663 });
664 }
665
666 if let Some(expected) = checksum {
667 let actual = fletcher32(&blob[14..blob_size]);
668 if actual != expected {
669 return Err(Error::ChecksumMismatch { expected, actual });
670 }
671 }
672
673 let info = BlobInfo {
674 version: Version::Lerc2(version),
675 data_type: DataType::from_code(image_type)?,
676 width,
677 height,
678 depth,
679 min_values: None,
680 max_values: None,
681 valid_pixel_count,
682 micro_block_size: micro_block_size as u32,
683 blob_size,
684 max_z_error,
685 z_min,
686 z_max,
687 };
688
689 Ok((info, cursor))
690}
691
692fn should_read_depth_ranges(info: &BlobInfo) -> bool {
693 matches!(info.version, Version::Lerc2(version) if version >= 4)
694 && info.valid_pixel_count != 0
695 && info.z_min != info.z_max
696}
697
698fn read_mask(
699 cursor: &mut Cursor<'_>,
700 info: &BlobInfo,
701 inherited_mask: Option<&[u8]>,
702) -> Result<Option<Vec<u8>>> {
703 let num_pixels = info.pixel_count()?;
704 let num_valid = info.valid_pixel_count as usize;
705 if num_valid > num_pixels {
706 return Err(Error::InvalidBlob(format!(
707 "valid pixel count {} exceeds pixel count {}",
708 num_valid, num_pixels
709 )));
710 }
711
712 let num_bytes = cursor.read_u32()? as usize;
713 if num_valid == num_pixels {
714 if num_bytes != 0 {
715 return Err(Error::InvalidBlob(
716 "full-valid LERC blob unexpectedly contains mask bytes".into(),
717 ));
718 }
719 return Ok(None);
720 }
721
722 if num_valid == 0 {
723 cursor.skip(num_bytes)?;
724 return Ok(Some(vec![0; num_pixels]));
725 }
726
727 if num_bytes == 0 {
728 let mask = inherited_mask.ok_or(Error::UnsupportedFeature(
729 "external masks are not yet supported",
730 ))?;
731 if mask.len() != num_pixels {
732 return Err(Error::InvalidBlob(
733 "inherited mask length does not match the current LERC blob".into(),
734 ));
735 }
736 let inherited_valid = mask.iter().filter(|&&value| value != 0).count();
737 if inherited_valid != num_valid {
738 return Err(Error::InvalidBlob(
739 "inherited mask valid count does not match the current LERC blob".into(),
740 ));
741 }
742 return Ok(Some(mask.to_vec()));
743 }
744
745 let bitset = decode_mask_rle(cursor.read_bytes(num_bytes)?, num_pixels.div_ceil(8))?;
746 Ok(Some(unpack_mask_bitset(&bitset, num_pixels)))
747}
748
749fn decode_mask_rle(encoded: &[u8], bitset_len: usize) -> Result<Vec<u8>> {
750 let mut bitset = vec![0u8; bitset_len];
751 let mut inner = Cursor::new(encoded);
752 let mut out = 0usize;
753
754 loop {
755 let count = inner.read_i16()?;
756 if count == i16::MIN {
757 break;
758 }
759 if count > 0 {
760 let count = count as usize;
761 let run = inner.read_bytes(count)?;
762 if out
763 .checked_add(count)
764 .ok_or_else(|| Error::InvalidBlob("mask RLE overflow".into()))?
765 > bitset.len()
766 {
767 return Err(Error::InvalidBlob(
768 "mask RLE expands past bitset length".into(),
769 ));
770 }
771 bitset[out..out + count].copy_from_slice(run);
772 out += count;
773 } else {
774 let count = (-count) as usize;
775 let value = inner.read_u8()?;
776 if out
777 .checked_add(count)
778 .ok_or_else(|| Error::InvalidBlob("mask RLE overflow".into()))?
779 > bitset.len()
780 {
781 return Err(Error::InvalidBlob(
782 "mask RLE expands past bitset length".into(),
783 ));
784 }
785 bitset[out..out + count].fill(value);
786 out += count;
787 }
788 }
789
790 if out != bitset.len() {
791 return Err(Error::InvalidBlob(
792 "mask RLE ended before filling bitset".into(),
793 ));
794 }
795 if inner.offset() != encoded.len() {
796 return Err(Error::InvalidBlob(
797 "mask RLE contains trailing bytes after sentinel".into(),
798 ));
799 }
800
801 Ok(bitset)
802}
803
804fn unpack_mask_bitset(bitset: &[u8], num_pixels: usize) -> Vec<u8> {
805 let mut mask = vec![0u8; num_pixels];
806 for (i, item) in mask.iter_mut().enumerate() {
807 let byte = bitset[i >> 3];
808 let bit = 7 - (i & 7);
809 *item = (byte >> bit) & 1;
810 }
811 mask
812}
813
814#[derive(Clone, Copy)]
815struct UnstuffOptions<'a> {
816 num_pixels: usize,
817 lut_values: Option<&'a [f64]>,
818 offset: Option<f64>,
819 scale: f64,
820 max_value: f64,
821}
822
823fn read_depth_ranges(cursor: &mut Cursor<'_>, info: &BlobInfo) -> Result<DepthRanges> {
824 let depth = info.depth as usize;
825 let range_bytes = depth
826 .checked_mul(info.data_type.byte_len())
827 .ok_or_else(|| Error::InvalidBlob("range byte length overflows usize".into()))?;
828 let min_values = read_typed_values(cursor.read_bytes(range_bytes)?, info.data_type)?;
829 let max_values = read_typed_values(cursor.read_bytes(range_bytes)?, info.data_type)?;
830 Ok(DepthRanges {
831 min_values,
832 max_values,
833 })
834}
835
836fn decode_pixels(
837 cursor: &mut Cursor<'_>,
838 info: &BlobInfo,
839 depth_ranges: Option<&DepthRanges>,
840 mask: Option<&[u8]>,
841) -> Result<PixelData> {
842 let num_pixels = info.pixel_count()?;
843 let sample_count = info.sample_count()?;
844
845 if info.valid_pixel_count == 0 {
846 return Ok(zeroed_pixels(info.data_type, sample_count));
847 }
848
849 if info.z_min == info.z_max {
850 return Ok(constant_pixels(
851 info.data_type,
852 num_pixels,
853 info.depth as usize,
854 mask,
855 ConstantValues::Single(info.z_max),
856 ));
857 }
858
859 if let Some(ranges) = depth_ranges {
860 if ranges.min_values == ranges.max_values {
861 return Ok(constant_pixels(
862 info.data_type,
863 num_pixels,
864 info.depth as usize,
865 mask,
866 ConstantValues::PerDepth(ranges.max_values.clone()),
867 ));
868 }
869 }
870
871 let one_sweep = cursor.read_u8()? != 0;
872 if one_sweep {
873 return decode_one_sweep(cursor, info, mask);
874 }
875
876 let version = match info.version {
877 Version::Lerc2(version) => version,
878 _ => unreachable!("Lerc2 decode called with a non-Lerc2 blob"),
879 };
880
881 if version > 1 && info.data_type.code() <= 1 && (info.max_z_error - 0.5).abs() < 1e-5 {
882 let encode_mode = cursor.read_u8()?;
883 if encode_mode > 2 || (version < 4 && encode_mode > 1) {
884 return Err(Error::InvalidBlob(format!(
885 "invalid Huffman flag {encode_mode}"
886 )));
887 }
888 if encode_mode != 0 {
889 return decode_huffman(cursor, info, depth_ranges, mask, encode_mode == 1);
890 }
891 }
892
893 decode_tiles(cursor, info, depth_ranges, mask)
894}
895
896fn decode_one_sweep(
897 cursor: &mut Cursor<'_>,
898 info: &BlobInfo,
899 mask: Option<&[u8]>,
900) -> Result<PixelData> {
901 let num_pixels = info.pixel_count()?;
902 let num_valid = info.valid_pixel_count as usize;
903 let depth = info.depth as usize;
904 let sample_len = info
905 .data_type
906 .byte_len()
907 .checked_mul(num_valid)
908 .and_then(|v| v.checked_mul(depth))
909 .ok_or_else(|| Error::InvalidBlob("one-sweep byte count overflows usize".into()))?;
910 let raw = read_typed_values(cursor.read_bytes(sample_len)?, info.data_type)?;
911
912 if num_valid == num_pixels {
913 return Ok(cast_pixels(info.data_type, raw));
914 }
915
916 let mask = mask.ok_or(Error::InvalidBlob(
917 "partial-valid one-sweep block is missing its decoded mask".into(),
918 ))?;
919 scatter_valid_samples(info.data_type, info.sample_count()?, depth, mask, &raw)
920}
921
922fn decode_tiles(
923 cursor: &mut Cursor<'_>,
924 info: &BlobInfo,
925 depth_ranges: Option<&DepthRanges>,
926 mask: Option<&[u8]>,
927) -> Result<PixelData> {
928 let width = info.width as usize;
929 let height = info.height as usize;
930 let depth = info.depth as usize;
931 let num_pixels = width
932 .checked_mul(height)
933 .ok_or_else(|| Error::InvalidBlob("pixel count overflows usize".into()))?;
934 let micro_block_size = info.micro_block_size as usize;
935 if micro_block_size == 0 {
936 return Err(Error::InvalidBlob(
937 "micro block size must be greater than zero".into(),
938 ));
939 }
940
941 let num_blocks_x = width.div_ceil(micro_block_size);
942 let num_blocks_y = height.div_ceil(micro_block_size);
943 let last_block_width = if width % micro_block_size == 0 {
944 micro_block_size
945 } else {
946 width % micro_block_size
947 };
948 let last_block_height = if height % micro_block_size == 0 {
949 micro_block_size
950 } else {
951 height % micro_block_size
952 };
953
954 let mut result_bsq = vec![0.0; info.sample_count()?];
955 let mut block_buffer = vec![0.0; micro_block_size * micro_block_size];
956 let version = match info.version {
957 Version::Lerc2(version) => version,
958 _ => unreachable!("Lerc2 tile decode called with a non-Lerc2 blob"),
959 };
960 let file_version_check_num = if version >= 5 { 14u8 } else { 15u8 };
961
962 for block_y in 0..num_blocks_y {
963 let this_block_height = if block_y + 1 == num_blocks_y {
964 last_block_height
965 } else {
966 micro_block_size
967 };
968 for block_x in 0..num_blocks_x {
969 let this_block_width = if block_x + 1 == num_blocks_x {
970 last_block_width
971 } else {
972 micro_block_size
973 };
974
975 for dim in 0..depth {
976 let header_byte = cursor.read_u8()?;
977 let is_diff_encoding = version >= 5 && (header_byte & 4) != 0;
978 let bits67 = header_byte >> 6;
979 let test_code = (header_byte >> 2) & file_version_check_num;
980 let expected_code =
981 (((block_x * micro_block_size) >> 3) as u8) & file_version_check_num;
982 if test_code != expected_code {
983 return Err(Error::InvalidBlob(
984 "tile block integrity check failed".into(),
985 ));
986 }
987 if is_diff_encoding && dim == 0 {
988 return Err(Error::InvalidBlob(
989 "diff-encoded tile block encountered in first dimension".into(),
990 ));
991 }
992
993 let block_encoding = header_byte & 3;
994 let dim_base = dim * num_pixels;
995 let prev_dim_base = dim.checked_sub(1).map(|prev| prev * num_pixels);
996 let z_max = depth_ranges
997 .and_then(|ranges| ranges.max_values.get(dim))
998 .copied()
999 .unwrap_or(info.z_max);
1000
1001 match block_encoding {
1002 2 => {
1003 if is_diff_encoding {
1004 let prev_dim_base = prev_dim_base.ok_or(Error::InvalidBlob(
1005 "diff encoding requires a previous dimension".into(),
1006 ))?;
1007 for row in 0..this_block_height {
1008 let pixel_row = block_y * micro_block_size + row;
1009 for col in 0..this_block_width {
1010 let pixel =
1011 pixel_row * width + block_x * micro_block_size + col;
1012 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1013 result_bsq[dim_base + pixel] =
1014 result_bsq[prev_dim_base + pixel];
1015 }
1016 }
1017 }
1018 }
1019 }
1020 0 => {
1021 if is_diff_encoding {
1022 return Err(Error::InvalidBlob(
1023 "uncompressed diff-encoded tile block is invalid".into(),
1024 ));
1025 }
1026 let values_needed = if let Some(mask) = mask {
1027 count_valid_in_block(
1028 mask,
1029 width,
1030 block_x * micro_block_size,
1031 block_y * micro_block_size,
1032 this_block_width,
1033 this_block_height,
1034 )
1035 } else {
1036 this_block_width * this_block_height
1037 };
1038 let byte_len = values_needed
1039 .checked_mul(info.data_type.byte_len())
1040 .ok_or_else(|| {
1041 Error::InvalidBlob(
1042 "uncompressed block byte length overflows usize".into(),
1043 )
1044 })?;
1045 let raw_values =
1046 read_typed_values(cursor.read_bytes(byte_len)?, info.data_type)?;
1047 let mut src = 0usize;
1048 for row in 0..this_block_height {
1049 let pixel_row = block_y * micro_block_size + row;
1050 for col in 0..this_block_width {
1051 let pixel = pixel_row * width + block_x * micro_block_size + col;
1052 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1053 result_bsq[dim_base + pixel] = raw_values[src];
1054 src += 1;
1055 }
1056 }
1057 }
1058 if src != raw_values.len() {
1059 return Err(Error::InvalidBlob(
1060 "uncompressed tile block payload contains trailing values".into(),
1061 ));
1062 }
1063 }
1064 1 | 3 => {
1065 let offset_type = data_type_used(
1066 if is_diff_encoding && info.data_type.code() < 6 {
1067 DataType::I32
1068 } else {
1069 info.data_type
1070 },
1071 bits67,
1072 )?;
1073 let offset =
1074 read_scalar(cursor.read_bytes(offset_type.byte_len())?, offset_type)?;
1075
1076 if block_encoding == 3 {
1077 for row in 0..this_block_height {
1078 let pixel_row = block_y * micro_block_size + row;
1079 for col in 0..this_block_width {
1080 let pixel =
1081 pixel_row * width + block_x * micro_block_size + col;
1082 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1083 result_bsq[dim_base + pixel] = if is_diff_encoding {
1084 let prev_dim_base =
1085 prev_dim_base.ok_or(Error::InvalidBlob(
1086 "diff encoding requires a previous dimension"
1087 .into(),
1088 ))?;
1089 (result_bsq[prev_dim_base + pixel] + offset).min(z_max)
1090 } else {
1091 offset
1092 };
1093 }
1094 }
1095 }
1096 } else {
1097 block_buffer.fill(0.0);
1098 let block_values = decode_bits(
1099 cursor,
1100 version,
1101 &mut block_buffer,
1102 Some(offset),
1103 info.max_z_error,
1104 z_max,
1105 )?;
1106 let mut src = 0usize;
1107 for row in 0..this_block_height {
1108 let pixel_row = block_y * micro_block_size + row;
1109 for col in 0..this_block_width {
1110 let pixel =
1111 pixel_row * width + block_x * micro_block_size + col;
1112 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1113 let value = block_buffer[src];
1114 result_bsq[dim_base + pixel] = if is_diff_encoding {
1115 let prev_dim_base =
1116 prev_dim_base.ok_or(Error::InvalidBlob(
1117 "diff encoding requires a previous dimension"
1118 .into(),
1119 ))?;
1120 value + result_bsq[prev_dim_base + pixel]
1121 } else {
1122 value
1123 };
1124 src += 1;
1125 }
1126 }
1127 }
1128 if src != block_values {
1129 return Err(Error::InvalidBlob(
1130 "bit-stuffed tile block value count does not match the block mask".into(),
1131 ));
1132 }
1133 }
1134 }
1135 _ => {
1136 return Err(Error::InvalidBlob(format!(
1137 "invalid tile block encoding {block_encoding}"
1138 )));
1139 }
1140 }
1141 }
1142 }
1143 }
1144
1145 let values = if depth > 1 {
1146 swap_bsq_to_bip(&result_bsq, num_pixels, depth)
1147 } else {
1148 result_bsq
1149 };
1150 Ok(cast_pixels(info.data_type, values))
1151}
1152
1153fn decode_huffman(
1154 cursor: &mut Cursor<'_>,
1155 info: &BlobInfo,
1156 depth_ranges: Option<&DepthRanges>,
1157 mask: Option<&[u8]>,
1158 delta_encode: bool,
1159) -> Result<PixelData> {
1160 let width = info.width as usize;
1161 let height = info.height as usize;
1162 let depth = info.depth as usize;
1163 let num_pixels = width
1164 .checked_mul(height)
1165 .ok_or_else(|| Error::InvalidBlob("pixel count overflows usize".into()))?;
1166 let mut result_bsq = vec![0.0; info.sample_count()?];
1167
1168 let huffman = read_huffman_tree(cursor, info)?;
1169 let mut stream = HuffmanStream {
1170 words: &huffman.stuffed_data,
1171 src_ptr: huffman.src_ptr,
1172 bit_pos: huffman.bit_pos,
1173 };
1174 if stream.bit_pos > 0 {
1175 stream.src_ptr += 1;
1176 stream.bit_pos = 0;
1177 }
1178
1179 let offset = if info.data_type == DataType::I8 {
1180 128.0
1181 } else {
1182 0.0
1183 };
1184
1185 if depth < 2 || delta_encode {
1186 for dim in 0..depth {
1187 let dim_base = dim * num_pixels;
1188 let mut prev_value = 0.0;
1189 for row in 0..height {
1190 for col in 0..width {
1191 let pixel = row * width + col;
1192 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1193 let value = read_huffman_symbol(&mut stream, &huffman)? as f64 - offset;
1194 if delta_encode {
1195 let mut delta = value;
1196 if col > 0 && mask.map(|m| m[pixel - 1] != 0).unwrap_or(true) {
1197 delta += prev_value;
1198 } else if row > 0 && mask.map(|m| m[pixel - width] != 0).unwrap_or(true)
1199 {
1200 delta += result_bsq[dim_base + pixel - width];
1201 } else {
1202 delta += prev_value;
1203 }
1204 let wrapped = ((delta as i64) & 0xFF) as f64;
1205 result_bsq[dim_base + pixel] = wrapped;
1206 prev_value = wrapped;
1207 } else {
1208 result_bsq[dim_base + pixel] = value;
1209 }
1210 }
1211 }
1212 }
1213 }
1214 } else {
1215 for row in 0..height {
1216 for col in 0..width {
1217 let pixel = row * width + col;
1218 if mask.map(|m| m[pixel] != 0).unwrap_or(true) {
1219 for dim in 0..depth {
1220 let dim_base = dim * num_pixels;
1221 result_bsq[dim_base + pixel] =
1222 read_huffman_symbol(&mut stream, &huffman)? as f64 - offset;
1223 }
1224 }
1225 }
1226 }
1227 }
1228
1229 let values = if depth > 1 {
1230 swap_bsq_to_bip(&result_bsq, num_pixels, depth)
1231 } else {
1232 result_bsq
1233 };
1234
1235 let _ = depth_ranges;
1236 Ok(cast_pixels(info.data_type, values))
1237}
1238
1239fn read_huffman_tree(cursor: &mut Cursor<'_>, info: &BlobInfo) -> Result<HuffmanInfo> {
1240 let version = read_i32(cursor.read_bytes(4)?)?;
1241 if version < 2 {
1242 return Err(Error::UnsupportedFeature("Huffman version < 2"));
1243 }
1244 let size = read_i32(cursor.read_bytes(4)?)?;
1245 let i0 = read_i32(cursor.read_bytes(4)?)?;
1246 let i1 = read_i32(cursor.read_bytes(4)?)?;
1247 if size <= 0 || i0 < 0 || i1 <= i0 {
1248 return Err(Error::InvalidBlob("invalid Huffman table header".into()));
1249 }
1250
1251 let mut code_lengths = vec![
1252 0.0;
1253 usize::try_from(i1 - i0).map_err(|_| {
1254 Error::InvalidBlob("Huffman code length count does not fit in memory".into())
1255 })?
1256 ];
1257 let _ = decode_bits(
1258 cursor,
1259 match info.version {
1260 Version::Lerc2(version) => version,
1261 _ => unreachable!("Lerc2 Huffman decode called with a non-Lerc2 blob"),
1262 },
1263 &mut code_lengths,
1264 None,
1265 info.max_z_error,
1266 info.z_max,
1267 )?;
1268
1269 let size = size as usize;
1270 let i0 = i0 as usize;
1271 let i1 = i1 as usize;
1272 let mut code_table: Vec<Option<(u8, u32)>> = vec![None; size];
1273 for i in i0..i1 {
1274 let j = if i < size { i } else { i - size };
1275 code_table[j] = Some((code_lengths[i - i0] as u8, 0));
1276 }
1277
1278 let stuffed_data = words_from_padded(cursor.read_bytes(cursor.remaining())?);
1279 let mut stream = HuffmanStream {
1280 words: &stuffed_data,
1281 src_ptr: 0,
1282 bit_pos: 0,
1283 };
1284
1285 for entry in code_table.iter_mut().flatten() {
1286 if entry.0 > 0 {
1287 entry.1 = stream.peek_bits(entry.0 as usize)?;
1288 stream.advance(entry.0 as usize)?;
1289 }
1290 }
1291
1292 let mut max_bits = 0u8;
1293 for (bit_len, _) in code_table.iter().flatten() {
1294 max_bits = max_bits.max(*bit_len);
1295 }
1296 let quick_bits = cmp::min(max_bits, HUFFMAN_LUT_BITS_MAX);
1297 let mut quick_lut = vec![None; 1usize << quick_bits];
1298 let mut tree = HuffmanNode::default();
1299
1300 for (symbol, entry) in code_table.into_iter().enumerate() {
1301 let Some((bit_len, code)) = entry else {
1302 continue;
1303 };
1304 if bit_len == 0 {
1305 continue;
1306 }
1307 if bit_len <= quick_bits {
1308 let base = code << (quick_bits - bit_len);
1309 let num_entries = 1usize << (quick_bits - bit_len);
1310 for extra in 0..num_entries {
1311 quick_lut[(base as usize) | extra] = Some(HuffmanEntry {
1312 bit_len,
1313 value: symbol as u32,
1314 });
1315 }
1316 } else {
1317 insert_huffman_code(&mut tree, code, bit_len, symbol as u32)?;
1318 }
1319 }
1320
1321 let src_ptr = stream.src_ptr;
1322 let bit_pos = stream.bit_pos;
1323
1324 Ok(HuffmanInfo {
1325 quick_lut,
1326 quick_bits,
1327 max_bits,
1328 tree,
1329 stuffed_data,
1330 src_ptr,
1331 bit_pos,
1332 })
1333}
1334
1335fn insert_huffman_code(root: &mut HuffmanNode, code: u32, bit_len: u8, value: u32) -> Result<()> {
1336 let mut node = root;
1337 for shift in (0..bit_len).rev() {
1338 let bit = (code >> shift) & 1;
1339 if shift == 0 {
1340 let child = if bit == 0 {
1341 node.left
1342 .get_or_insert_with(|| Box::new(HuffmanNode::default()))
1343 } else {
1344 node.right
1345 .get_or_insert_with(|| Box::new(HuffmanNode::default()))
1346 };
1347 child.value = Some(value);
1348 } else {
1349 node = if bit == 0 {
1350 node.left
1351 .get_or_insert_with(|| Box::new(HuffmanNode::default()))
1352 } else {
1353 node.right
1354 .get_or_insert_with(|| Box::new(HuffmanNode::default()))
1355 };
1356 }
1357 }
1358 Ok(())
1359}
1360
1361fn read_huffman_symbol(stream: &mut HuffmanStream<'_>, info: &HuffmanInfo) -> Result<u32> {
1362 if let Some(entry) =
1363 info.quick_lut[stream.peek_bits(info.quick_bits as usize)? as usize].as_ref()
1364 {
1365 stream.advance(entry.bit_len as usize)?;
1366 return Ok(entry.value);
1367 }
1368
1369 let value = stream.peek_bits(info.max_bits as usize)?;
1370 let mut node = &info.tree;
1371 for used_bits in 0..info.max_bits {
1372 let bit = (value >> (info.max_bits - used_bits - 1)) & 1;
1373 node = if bit == 0 {
1374 node.left.as_deref().ok_or_else(|| {
1375 Error::InvalidBlob("Huffman decode walked a missing left node".into())
1376 })?
1377 } else {
1378 node.right.as_deref().ok_or_else(|| {
1379 Error::InvalidBlob("Huffman decode walked a missing right node".into())
1380 })?
1381 };
1382
1383 if node.left.is_none() && node.right.is_none() {
1384 let symbol = node
1385 .value
1386 .ok_or_else(|| Error::InvalidBlob("Huffman leaf is missing a symbol".into()))?;
1387 stream.advance((used_bits + 1) as usize)?;
1388 return Ok(symbol);
1389 }
1390 }
1391
1392 Err(Error::InvalidBlob(
1393 "Huffman symbol exceeded the configured code width".into(),
1394 ))
1395}
1396
1397fn decode_bits(
1398 cursor: &mut Cursor<'_>,
1399 version: u32,
1400 out: &mut [f64],
1401 offset: Option<f64>,
1402 max_z_error: f64,
1403 max_value: f64,
1404) -> Result<usize> {
1405 let header_byte = cursor.read_u8()?;
1406 let bits67 = header_byte >> 6;
1407 let count_bytes = if bits67 == 0 { 4 } else { 3 - bits67 as usize };
1408 let do_lut = (header_byte & 32) != 0;
1409 let num_bits = header_byte & 31;
1410
1411 let num_elements = match count_bytes {
1412 1 => cursor.read_u8()? as usize,
1413 2 => read_u16(cursor.read_bytes(2)?)? as usize,
1414 4 => cursor.read_u32()? as usize,
1415 _ => {
1416 return Err(Error::InvalidBlob(
1417 "invalid valid pixel count field width".into(),
1418 ))
1419 }
1420 };
1421 if num_elements > out.len() {
1422 return Err(Error::InvalidBlob(
1423 "bit-stuffed block expands beyond its output buffer".into(),
1424 ));
1425 }
1426
1427 if do_lut {
1428 let offset = offset.ok_or(Error::InvalidBlob(
1429 "LUT-compressed block is missing its base offset".into(),
1430 ))?;
1431 let lut_bytes = cursor.read_u8()? as usize;
1432 if lut_bytes == 0 {
1433 return Err(Error::InvalidBlob(
1434 "LUT-compressed block has zero LUT bytes".into(),
1435 ));
1436 }
1437 let lut_data_bytes = ((lut_bytes - 1) * usize::from(num_bits)).div_ceil(8);
1438 let lut_words = words_from_padded(cursor.read_bytes(lut_data_bytes)?);
1439 let lut_bits_per_element = bits_required(lut_bytes - 1);
1440 let stuffed_data_bytes = (num_elements * usize::from(lut_bits_per_element)).div_ceil(8);
1441 let stuffed_words = words_from_padded(cursor.read_bytes(stuffed_data_bytes)?);
1442
1443 let lut_values = if version >= 3 {
1444 unstuff_lut_v3(
1445 &lut_words,
1446 num_bits,
1447 lut_bytes - 1,
1448 offset,
1449 2.0 * max_z_error,
1450 max_value,
1451 )
1452 } else {
1453 unstuff_lut_v2(
1454 &lut_words,
1455 num_bits,
1456 lut_bytes - 1,
1457 offset,
1458 2.0 * max_z_error,
1459 max_value,
1460 )
1461 };
1462
1463 if version >= 3 {
1464 unstuff_v3(
1465 &stuffed_words,
1466 &mut out[..num_elements],
1467 lut_bits_per_element,
1468 UnstuffOptions {
1469 num_pixels: num_elements,
1470 lut_values: Some(&lut_values),
1471 offset: None,
1472 scale: 0.0,
1473 max_value,
1474 },
1475 );
1476 } else {
1477 unstuff_v2(
1478 &stuffed_words,
1479 &mut out[..num_elements],
1480 lut_bits_per_element,
1481 UnstuffOptions {
1482 num_pixels: num_elements,
1483 lut_values: Some(&lut_values),
1484 offset: None,
1485 scale: 0.0,
1486 max_value,
1487 },
1488 );
1489 }
1490 return Ok(num_elements);
1491 }
1492
1493 if num_bits == 0 {
1494 out[..num_elements].fill(offset.unwrap_or(0.0));
1495 return Ok(num_elements);
1496 }
1497
1498 let stuffed_data_bytes = (num_elements * usize::from(num_bits)).div_ceil(8);
1499 let stuffed_words = words_from_padded(cursor.read_bytes(stuffed_data_bytes)?);
1500 match (version >= 3, offset) {
1501 (true, Some(offset)) => unstuff_v3(
1502 &stuffed_words,
1503 &mut out[..num_elements],
1504 num_bits,
1505 UnstuffOptions {
1506 num_pixels: num_elements,
1507 lut_values: None,
1508 offset: Some(offset),
1509 scale: 2.0 * max_z_error,
1510 max_value,
1511 },
1512 ),
1513 (true, None) => original_unstuff_v3(&stuffed_words, &mut out[..num_elements], num_bits),
1514 (false, Some(offset)) => unstuff_v2(
1515 &stuffed_words,
1516 &mut out[..num_elements],
1517 num_bits,
1518 UnstuffOptions {
1519 num_pixels: num_elements,
1520 lut_values: None,
1521 offset: Some(offset),
1522 scale: 2.0 * max_z_error,
1523 max_value,
1524 },
1525 ),
1526 (false, None) => original_unstuff_v2(&stuffed_words, &mut out[..num_elements], num_bits),
1527 }
1528 Ok(num_elements)
1529}
1530
1531fn unstuff_v2(src: &[u32], dest: &mut [f64], bits_per_pixel: u8, options: UnstuffOptions<'_>) {
1532 let bit_mask = if bits_per_pixel == 32 {
1533 u32::MAX
1534 } else {
1535 (1u32 << bits_per_pixel) - 1
1536 };
1537 let mut words = src.to_vec();
1538 let num_invalid_tail_bytes =
1539 words.len() * 4 - (usize::from(bits_per_pixel) * options.num_pixels).div_ceil(8);
1540 if let Some(last) = words.last_mut() {
1541 *last <<= 8 * num_invalid_tail_bytes;
1542 }
1543
1544 let mut index = 0usize;
1545 let mut bits_left = 0usize;
1546 let mut buffer = 0u32;
1547 let nmax = options
1548 .offset
1549 .map(|offset| quantized_nmax(offset, options.scale, options.max_value));
1550
1551 for item in dest.iter_mut().take(options.num_pixels) {
1552 if bits_left == 0 {
1553 buffer = words[index];
1554 index += 1;
1555 bits_left = 32;
1556 }
1557 let n = if bits_left >= bits_per_pixel as usize {
1558 let n = (buffer >> (bits_left - bits_per_pixel as usize)) & bit_mask;
1559 bits_left -= bits_per_pixel as usize;
1560 n
1561 } else {
1562 let missing_bits = bits_per_pixel as usize - bits_left;
1563 let mut n = ((buffer & bit_mask) << missing_bits) & bit_mask;
1564 buffer = words[index];
1565 index += 1;
1566 bits_left = 32 - missing_bits;
1567 n += buffer >> bits_left;
1568 n
1569 };
1570 *item = match (options.lut_values, options.offset) {
1571 (Some(lut_values), _) => lut_values[n as usize],
1572 (None, Some(offset)) => {
1573 quantized_value(offset, options.scale, options.max_value, n, nmax.unwrap())
1574 }
1575 (None, None) => n as f64,
1576 };
1577 }
1578}
1579
1580fn unstuff_v3(src: &[u32], dest: &mut [f64], bits_per_pixel: u8, options: UnstuffOptions<'_>) {
1581 let bit_mask = if bits_per_pixel == 32 {
1582 u32::MAX
1583 } else {
1584 (1u32 << bits_per_pixel) - 1
1585 };
1586 let mut index = 0usize;
1587 let mut bits_left = 0usize;
1588 let mut bit_pos = 0usize;
1589 let mut buffer = 0u32;
1590 let nmax = options
1591 .offset
1592 .map(|offset| quantized_nmax(offset, options.scale, options.max_value));
1593
1594 for item in dest.iter_mut().take(options.num_pixels) {
1595 if bits_left == 0 {
1596 buffer = src[index];
1597 index += 1;
1598 bits_left = 32;
1599 bit_pos = 0;
1600 }
1601 let n = if bits_left >= bits_per_pixel as usize {
1602 let n = (buffer >> bit_pos) & bit_mask;
1603 bits_left -= bits_per_pixel as usize;
1604 bit_pos += bits_per_pixel as usize;
1605 n
1606 } else {
1607 let missing_bits = bits_per_pixel as usize - bits_left;
1608 let mut n = (buffer >> bit_pos) & bit_mask;
1609 buffer = src[index];
1610 index += 1;
1611 bits_left = 32 - missing_bits;
1612 n |=
1613 (buffer & ((1u32 << missing_bits) - 1)) << (bits_per_pixel as usize - missing_bits);
1614 bit_pos = missing_bits;
1615 n
1616 };
1617 *item = match (options.lut_values, options.offset) {
1618 (Some(lut_values), _) => lut_values[n as usize],
1619 (None, Some(offset)) => {
1620 quantized_value(offset, options.scale, options.max_value, n, nmax.unwrap())
1621 }
1622 (None, None) => n as f64,
1623 };
1624 }
1625}
1626
1627fn original_unstuff_v2(src: &[u32], dest: &mut [f64], bits_per_pixel: u8) {
1628 unstuff_v2(
1629 src,
1630 dest,
1631 bits_per_pixel,
1632 UnstuffOptions {
1633 num_pixels: dest.len(),
1634 lut_values: None,
1635 offset: None,
1636 scale: 0.0,
1637 max_value: 0.0,
1638 },
1639 );
1640}
1641
1642fn original_unstuff_v3(src: &[u32], dest: &mut [f64], bits_per_pixel: u8) {
1643 unstuff_v3(
1644 src,
1645 dest,
1646 bits_per_pixel,
1647 UnstuffOptions {
1648 num_pixels: dest.len(),
1649 lut_values: None,
1650 offset: None,
1651 scale: 0.0,
1652 max_value: 0.0,
1653 },
1654 );
1655}
1656
1657fn unstuff_lut_v2(
1658 src: &[u32],
1659 bits_per_pixel: u8,
1660 num_pixels: usize,
1661 offset: f64,
1662 scale: f64,
1663 max_value: f64,
1664) -> Vec<f64> {
1665 let mut values = vec![0.0; num_pixels];
1666 unstuff_v2(
1667 src,
1668 &mut values,
1669 bits_per_pixel,
1670 UnstuffOptions {
1671 num_pixels,
1672 lut_values: None,
1673 offset: Some(offset),
1674 scale,
1675 max_value,
1676 },
1677 );
1678 let mut out = Vec::with_capacity(values.len() + 1);
1679 out.push(offset);
1680 out.extend(values);
1681 out
1682}
1683
1684fn unstuff_lut_v3(
1685 src: &[u32],
1686 bits_per_pixel: u8,
1687 num_pixels: usize,
1688 offset: f64,
1689 scale: f64,
1690 max_value: f64,
1691) -> Vec<f64> {
1692 let mut values = vec![0.0; num_pixels];
1693 unstuff_v3(
1694 src,
1695 &mut values,
1696 bits_per_pixel,
1697 UnstuffOptions {
1698 num_pixels,
1699 lut_values: None,
1700 offset: Some(offset),
1701 scale,
1702 max_value,
1703 },
1704 );
1705 let mut out = Vec::with_capacity(values.len() + 1);
1706 out.push(offset);
1707 out.extend(values);
1708 out
1709}
1710
1711fn quantized_nmax(offset: f64, scale: f64, max_value: f64) -> f64 {
1712 if scale == 0.0 {
1713 f64::INFINITY
1714 } else {
1715 ((max_value - offset) / scale).ceil()
1716 }
1717}
1718
1719fn quantized_value(offset: f64, scale: f64, max_value: f64, n: u32, nmax: f64) -> f64 {
1720 if (n as f64) < nmax {
1721 offset + (n as f64) * scale
1722 } else {
1723 max_value
1724 }
1725}
1726
1727fn data_type_used(data_type: DataType, tc: u8) -> Result<DataType> {
1728 let code = match data_type.code() {
1729 2 | 4 => i32::from(data_type.code()) - i32::from(tc),
1730 3 | 5 => i32::from(data_type.code()) - 2 * i32::from(tc),
1731 6 => {
1732 if tc == 0 {
1733 6
1734 } else if tc == 1 {
1735 2
1736 } else {
1737 1
1738 }
1739 }
1740 7 => {
1741 if tc == 0 {
1742 7
1743 } else {
1744 7 - 2 * i32::from(tc) + 1
1745 }
1746 }
1747 _ => i32::from(data_type.code()),
1748 };
1749 DataType::from_code(code)
1750}
1751
1752fn bits_required(max_index: usize) -> u8 {
1753 let mut bits = 0u8;
1754 let mut value = max_index;
1755 while value > 0 {
1756 bits += 1;
1757 value >>= 1;
1758 }
1759 bits
1760}
1761
1762fn words_from_padded(bytes: &[u8]) -> Vec<u32> {
1763 if bytes.is_empty() {
1764 return Vec::new();
1765 }
1766 let mut padded = vec![0u8; bytes.len().div_ceil(4) * 4];
1767 padded[..bytes.len()].copy_from_slice(bytes);
1768 padded
1769 .chunks_exact(4)
1770 .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
1771 .collect()
1772}
1773
1774impl<'a> HuffmanStream<'a> {
1775 fn peek_bits(&self, num_bits: usize) -> Result<u32> {
1776 if num_bits == 0 {
1777 return Ok(0);
1778 }
1779 if self.src_ptr >= self.words.len() {
1780 return Err(Error::Truncated {
1781 offset: self.src_ptr * 4,
1782 needed: 4,
1783 available: 0,
1784 });
1785 }
1786 let word = self.words[self.src_ptr];
1787 let mut value = word.wrapping_shl(self.bit_pos as u32) >> (32 - num_bits as u32);
1788 if 32 - (self.bit_pos as usize) < num_bits {
1789 let next = self.words.get(self.src_ptr + 1).copied().unwrap_or(0);
1790 value |= next >> (64 - self.bit_pos as usize - num_bits);
1791 }
1792 Ok(value)
1793 }
1794
1795 fn advance(&mut self, bits: usize) -> Result<()> {
1796 let total = self.bit_pos as usize + bits;
1797 self.src_ptr = self
1798 .src_ptr
1799 .checked_add(total / 32)
1800 .ok_or_else(|| Error::InvalidBlob("Huffman bitstream pointer overflow".into()))?;
1801 self.bit_pos = (total % 32) as u8;
1802 Ok(())
1803 }
1804}
1805
1806fn count_valid_in_block(
1807 mask: &[u8],
1808 width: usize,
1809 x0: usize,
1810 y0: usize,
1811 block_width: usize,
1812 block_height: usize,
1813) -> usize {
1814 let mut count = 0usize;
1815 for row in 0..block_height {
1816 let row_offset = (y0 + row) * width + x0;
1817 for col in 0..block_width {
1818 count += usize::from(mask[row_offset + col] != 0);
1819 }
1820 }
1821 count
1822}
1823
1824fn read_typed_values(bytes: &[u8], data_type: DataType) -> Result<Vec<f64>> {
1825 let mut out = Vec::with_capacity(bytes.len() / data_type.byte_len());
1826 let mut cursor = Cursor::new(bytes);
1827 while cursor.offset() < bytes.len() {
1828 out.push(read_scalar(
1829 cursor.read_bytes(data_type.byte_len())?,
1830 data_type,
1831 )?);
1832 }
1833 Ok(out)
1834}
1835
1836fn read_scalar(bytes: &[u8], data_type: DataType) -> Result<f64> {
1837 Ok(match data_type {
1838 DataType::I8 => i8::from_le_bytes([bytes[0]]) as f64,
1839 DataType::U8 => bytes[0] as f64,
1840 DataType::I16 => i16::from_le_bytes(bytes.try_into().unwrap()) as f64,
1841 DataType::U16 => u16::from_le_bytes(bytes.try_into().unwrap()) as f64,
1842 DataType::I32 => i32::from_le_bytes(bytes.try_into().unwrap()) as f64,
1843 DataType::U32 => u32::from_le_bytes(bytes.try_into().unwrap()) as f64,
1844 DataType::F32 => f32::from_le_bytes(bytes.try_into().unwrap()) as f64,
1845 DataType::F64 => f64::from_le_bytes(bytes.try_into().unwrap()),
1846 })
1847}
1848
1849fn read_u16(bytes: &[u8]) -> Result<u16> {
1850 Ok(u16::from_le_bytes(bytes.try_into().unwrap()))
1851}
1852
1853fn read_i32(bytes: &[u8]) -> Result<i32> {
1854 Ok(i32::from_le_bytes(bytes.try_into().unwrap()))
1855}
1856
1857fn swap_bsq_to_bip(values: &[f64], num_pixels: usize, depth: usize) -> Vec<f64> {
1858 let mut out = vec![0.0; values.len()];
1859 let mut dest = 0usize;
1860 for pixel in 0..num_pixels {
1861 let mut src = pixel;
1862 for _ in 0..depth {
1863 out[dest] = values[src];
1864 dest += 1;
1865 src += num_pixels;
1866 }
1867 }
1868 out
1869}
1870
1871enum ConstantValues {
1872 Single(f64),
1873 PerDepth(Vec<f64>),
1874}
1875
1876fn constant_pixels(
1877 data_type: DataType,
1878 num_pixels: usize,
1879 depth: usize,
1880 mask: Option<&[u8]>,
1881 values: ConstantValues,
1882) -> PixelData {
1883 let total = num_pixels * depth;
1884 let mut out = vec![0.0; total];
1885 match (depth, values) {
1886 (_, ConstantValues::Single(value)) => {
1887 if let Some(mask) = mask {
1888 for (pixel, &mask_value) in mask.iter().enumerate().take(num_pixels) {
1889 if mask_value != 0 {
1890 let base = pixel * depth;
1891 out[base..base + depth].fill(value);
1892 }
1893 }
1894 } else {
1895 out.fill(value);
1896 }
1897 }
1898 (depth, ConstantValues::PerDepth(values)) => {
1899 if let Some(mask) = mask {
1900 for (pixel, &mask_value) in mask.iter().enumerate().take(num_pixels) {
1901 if mask_value != 0 {
1902 let base = pixel * depth;
1903 out[base..base + depth].copy_from_slice(&values);
1904 }
1905 }
1906 } else {
1907 for pixel in 0..num_pixels {
1908 let base = pixel * depth;
1909 out[base..base + depth].copy_from_slice(&values);
1910 }
1911 }
1912 }
1913 }
1914 cast_pixels(data_type, out)
1915}
1916
1917fn scatter_valid_samples(
1918 data_type: DataType,
1919 total_samples: usize,
1920 depth: usize,
1921 mask: &[u8],
1922 valid_samples: &[f64],
1923) -> Result<PixelData> {
1924 let mut out = vec![0.0; total_samples];
1925 let mut src = 0usize;
1926 for (pixel, &is_valid) in mask.iter().enumerate() {
1927 if is_valid == 0 {
1928 continue;
1929 }
1930 let base = pixel * depth;
1931 let end = base + depth;
1932 let src_end = src + depth;
1933 if src_end > valid_samples.len() {
1934 return Err(Error::InvalidBlob(
1935 "one-sweep valid sample payload is too short".into(),
1936 ));
1937 }
1938 out[base..end].copy_from_slice(&valid_samples[src..src_end]);
1939 src = src_end;
1940 }
1941 if src != valid_samples.len() {
1942 return Err(Error::InvalidBlob(
1943 "one-sweep valid sample payload contains trailing values".into(),
1944 ));
1945 }
1946 Ok(cast_pixels(data_type, out))
1947}
1948
1949fn zeroed_pixels(data_type: DataType, total_samples: usize) -> PixelData {
1950 match data_type {
1951 DataType::I8 => PixelData::I8(vec![0; total_samples]),
1952 DataType::U8 => PixelData::U8(vec![0; total_samples]),
1953 DataType::I16 => PixelData::I16(vec![0; total_samples]),
1954 DataType::U16 => PixelData::U16(vec![0; total_samples]),
1955 DataType::I32 => PixelData::I32(vec![0; total_samples]),
1956 DataType::U32 => PixelData::U32(vec![0; total_samples]),
1957 DataType::F32 => PixelData::F32(vec![0.0; total_samples]),
1958 DataType::F64 => PixelData::F64(vec![0.0; total_samples]),
1959 }
1960}
1961
1962fn cast_pixels(data_type: DataType, values: Vec<f64>) -> PixelData {
1963 match data_type {
1964 DataType::I8 => PixelData::I8(values.into_iter().map(|x| x as i8).collect()),
1965 DataType::U8 => PixelData::U8(values.into_iter().map(|x| x as u8).collect()),
1966 DataType::I16 => PixelData::I16(values.into_iter().map(|x| x as i16).collect()),
1967 DataType::U16 => PixelData::U16(values.into_iter().map(|x| x as u16).collect()),
1968 DataType::I32 => PixelData::I32(values.into_iter().map(|x| x as i32).collect()),
1969 DataType::U32 => PixelData::U32(values.into_iter().map(|x| x as u32).collect()),
1970 DataType::F32 => PixelData::F32(values.into_iter().map(|x| x as f32).collect()),
1971 DataType::F64 => PixelData::F64(values),
1972 }
1973}
1974
1975fn scan_range(pixels: &PixelData, mask: Option<&[u8]>) -> Result<(f64, f64)> {
1976 let values = pixels.to_f64();
1977 let mut min_value = f64::INFINITY;
1978 let mut max_value = f64::NEG_INFINITY;
1979
1980 for (index, value) in values.into_iter().enumerate() {
1981 if mask.map(|mask| mask[index] == 0).unwrap_or(false) {
1982 continue;
1983 }
1984 min_value = min_value.min(value);
1985 max_value = max_value.max(value);
1986 }
1987
1988 if !min_value.is_finite() || !max_value.is_finite() {
1989 return Err(Error::InvalidBlob(
1990 "cannot compute a value range for an empty LERC pixel buffer".into(),
1991 ));
1992 }
1993
1994 Ok((min_value, max_value))
1995}
1996
1997fn fletcher32(bytes: &[u8]) -> u32 {
1998 let mut sum1 = 0xffffu32;
1999 let mut sum2 = 0xffffu32;
2000 let mut words = bytes.len() / 2;
2001 let mut index = 0usize;
2002
2003 while words > 0 {
2004 let chunk = words.min(359);
2005 words -= chunk;
2006 for _ in 0..chunk {
2007 sum1 += (bytes[index] as u32) << 8;
2008 index += 1;
2009 sum2 += sum1 + bytes[index] as u32;
2010 sum1 += bytes[index] as u32;
2011 index += 1;
2012 }
2013 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
2014 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
2015 }
2016
2017 if bytes.len() & 1 != 0 {
2018 sum1 += (bytes[index] as u32) << 8;
2019 sum2 += sum1;
2020 }
2021
2022 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
2023 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
2024 (sum2 << 16) | (sum1 & 0xffff)
2025}
2026
2027#[cfg(test)]
2028mod tests {
2029 use super::*;
2030 use ndarray::IxDyn;
2031
2032 #[allow(clippy::too_many_arguments)]
2033 fn build_header_v2(
2034 width: u32,
2035 height: u32,
2036 valid_pixel_count: u32,
2037 image_type: i32,
2038 max_z_error: f64,
2039 z_min: f64,
2040 z_max: f64,
2041 payload_len: usize,
2042 ) -> Vec<u8> {
2043 let blob_size = 58 + 4 + payload_len;
2044 let mut bytes = Vec::with_capacity(blob_size);
2045 bytes.extend_from_slice(MAGIC_LERC2);
2046 bytes.extend_from_slice(&2i32.to_le_bytes());
2047 bytes.extend_from_slice(&height.to_le_bytes());
2048 bytes.extend_from_slice(&width.to_le_bytes());
2049 bytes.extend_from_slice(&valid_pixel_count.to_le_bytes());
2050 bytes.extend_from_slice(&8i32.to_le_bytes());
2051 bytes.extend_from_slice(&(blob_size as i32).to_le_bytes());
2052 bytes.extend_from_slice(&image_type.to_le_bytes());
2053 bytes.extend_from_slice(&max_z_error.to_le_bytes());
2054 bytes.extend_from_slice(&z_min.to_le_bytes());
2055 bytes.extend_from_slice(&z_max.to_le_bytes());
2056 bytes
2057 }
2058
2059 fn finalize_v4_with_checksum(mut bytes: Vec<u8>) -> Vec<u8> {
2060 let blob_size = bytes.len() as i32;
2061 bytes[34..38].copy_from_slice(&blob_size.to_le_bytes());
2062 let checksum = fletcher32(&bytes[14..blob_size as usize]);
2063 bytes[10..14].copy_from_slice(&checksum.to_le_bytes());
2064 bytes
2065 }
2066
2067 fn encode_mask_rle(mask: &[u8]) -> Vec<u8> {
2068 let bitset_len = mask.len().div_ceil(8);
2069 let mut bitset = vec![0u8; bitset_len];
2070 for (index, &value) in mask.iter().enumerate() {
2071 if value != 0 {
2072 bitset[index >> 3] |= 1 << (7 - (index & 7));
2073 }
2074 }
2075
2076 let mut encoded = Vec::with_capacity(bitset_len + 4);
2077 encoded.extend_from_slice(&(bitset_len as i16).to_le_bytes());
2078 encoded.extend_from_slice(&bitset);
2079 encoded.extend_from_slice(&i16::MIN.to_le_bytes());
2080 encoded
2081 }
2082
2083 fn pack_msb_bits(values: &[u32], bits_per_pixel: u8) -> Vec<u8> {
2084 let total_bits = values.len() * usize::from(bits_per_pixel);
2085 let mut bytes = vec![0u8; total_bits.div_ceil(8)];
2086 let mut bit_offset = 0usize;
2087 for &value in values {
2088 for bit in (0..bits_per_pixel).rev() {
2089 if ((value >> bit) & 1) != 0 {
2090 let byte_index = bit_offset / 8;
2091 let bit_index = 7 - (bit_offset % 8);
2092 bytes[byte_index] |= 1 << bit_index;
2093 }
2094 bit_offset += 1;
2095 }
2096 }
2097 bytes
2098 }
2099
2100 fn build_lerc1_blob(
2101 include_mask_header: bool,
2102 mask: Option<&[u8]>,
2103 values: &[f32],
2104 max_z_error: f64,
2105 max_value: f32,
2106 ) -> Vec<u8> {
2107 let width = 2u32;
2108 let height = 2u32;
2109 let mut bytes = Vec::new();
2110 bytes.extend_from_slice(b"CntZImage ");
2111 bytes.extend_from_slice(&11i32.to_le_bytes());
2112 bytes.extend_from_slice(&0i32.to_le_bytes());
2113 bytes.extend_from_slice(&height.to_le_bytes());
2114 bytes.extend_from_slice(&width.to_le_bytes());
2115 bytes.extend_from_slice(&max_z_error.to_le_bytes());
2116
2117 if include_mask_header {
2118 if let Some(mask) = mask {
2119 let encoded_mask = encode_mask_rle(mask);
2120 bytes.extend_from_slice(&1u32.to_le_bytes());
2121 bytes.extend_from_slice(&1u32.to_le_bytes());
2122 bytes.extend_from_slice(&(encoded_mask.len() as u32).to_le_bytes());
2123 bytes.extend_from_slice(&1.0f32.to_le_bytes());
2124 bytes.extend_from_slice(&encoded_mask);
2125 } else {
2126 bytes.extend_from_slice(&1u32.to_le_bytes());
2127 bytes.extend_from_slice(&1u32.to_le_bytes());
2128 bytes.extend_from_slice(&0u32.to_le_bytes());
2129 bytes.extend_from_slice(&1.0f32.to_le_bytes());
2130 }
2131 }
2132
2133 let valid_count = mask
2134 .map(|mask| mask.iter().filter(|&&value| value != 0).count())
2135 .unwrap_or(values.len());
2136 let offset = values.iter().copied().reduce(f32::min).unwrap_or(0.0);
2137 let quantized: Vec<u32> = values
2138 .iter()
2139 .map(|&value| ((value - offset) / (2.0 * max_z_error as f32)).round() as u32)
2140 .collect();
2141 let max_quantized = quantized.iter().copied().max().unwrap_or(0);
2142 let bits_per_pixel = bits_required(max_quantized as usize).max(1);
2143 let payload = pack_msb_bits(&quantized, bits_per_pixel);
2144 let pixel_section_len = 1 + 4 + 1 + 1 + payload.len();
2145 bytes.extend_from_slice(&1u32.to_le_bytes());
2146 bytes.extend_from_slice(&1u32.to_le_bytes());
2147 bytes.extend_from_slice(&(pixel_section_len as u32).to_le_bytes());
2148 bytes.extend_from_slice(&max_value.to_le_bytes());
2149 bytes.push(1);
2150 bytes.extend_from_slice(&offset.to_le_bytes());
2151 bytes.push((bits_per_pixel & 63) | (2 << 6));
2152 bytes.push(valid_count as u8);
2153 bytes.extend_from_slice(&payload);
2154 bytes
2155 }
2156
2157 #[test]
2158 fn reads_blob_info_for_constant_lerc2() {
2159 let mut blob = build_header_v2(3, 2, 6, 3, 0.0, 7.0, 7.0, 0);
2160 blob.extend_from_slice(&0u32.to_le_bytes());
2161
2162 let info = get_blob_info(&blob).unwrap();
2163 assert_eq!(info.width, 3);
2164 assert_eq!(info.height, 2);
2165 assert_eq!(info.depth, 1);
2166 assert_eq!(info.data_type, DataType::U16);
2167 assert_eq!(info.blob_size, blob.len());
2168 }
2169
2170 #[test]
2171 fn decodes_constant_surface() {
2172 let mut blob = build_header_v2(2, 2, 4, 1, 0.0, 9.0, 9.0, 0);
2173 blob.extend_from_slice(&0u32.to_le_bytes());
2174
2175 let decoded = decode(&blob).unwrap();
2176 assert_eq!(decoded.mask, None);
2177 assert_eq!(decoded.pixels, PixelData::U8(vec![9, 9, 9, 9]));
2178 }
2179
2180 #[test]
2181 fn decodes_one_sweep_all_valid() {
2182 let mut blob = build_header_v2(2, 2, 4, 1, 0.0, 1.0, 4.0, 1 + 4);
2183 blob.extend_from_slice(&0u32.to_le_bytes());
2184 blob.push(1);
2185 blob.extend_from_slice(&[1, 2, 3, 4]);
2186
2187 let decoded = decode(&blob).unwrap();
2188 assert_eq!(decoded.pixels, PixelData::U8(vec![1, 2, 3, 4]));
2189 }
2190
2191 #[test]
2192 fn decodes_constant_surface_with_per_depth_ranges() {
2193 let mut bytes = Vec::new();
2194 bytes.extend_from_slice(MAGIC_LERC2);
2195 bytes.extend_from_slice(&4i32.to_le_bytes());
2196 bytes.extend_from_slice(&0u32.to_le_bytes());
2197 bytes.extend_from_slice(&1u32.to_le_bytes());
2198 bytes.extend_from_slice(&2u32.to_le_bytes());
2199 bytes.extend_from_slice(&2u32.to_le_bytes());
2200 bytes.extend_from_slice(&2u32.to_le_bytes());
2201 bytes.extend_from_slice(&8i32.to_le_bytes());
2202 bytes.extend_from_slice(&0i32.to_le_bytes());
2203 bytes.extend_from_slice(&6i32.to_le_bytes());
2204 bytes.extend_from_slice(&0.0f64.to_le_bytes());
2205 bytes.extend_from_slice(&10.0f64.to_le_bytes());
2206 bytes.extend_from_slice(&20.0f64.to_le_bytes());
2207 bytes.extend_from_slice(&0u32.to_le_bytes());
2208 bytes.extend_from_slice(&10.0f32.to_le_bytes());
2209 bytes.extend_from_slice(&20.0f32.to_le_bytes());
2210 bytes.extend_from_slice(&10.0f32.to_le_bytes());
2211 bytes.extend_from_slice(&20.0f32.to_le_bytes());
2212
2213 let blob = finalize_v4_with_checksum(bytes);
2214 let decoded = decode(&blob).unwrap();
2215 assert_eq!(decoded.pixels, PixelData::F32(vec![10.0, 20.0, 10.0, 20.0]));
2216 }
2217
2218 #[test]
2219 fn counts_concatenated_bands() {
2220 let mut blob1 = build_header_v2(1, 1, 1, 1, 0.0, 3.0, 3.0, 0);
2221 blob1.extend_from_slice(&0u32.to_le_bytes());
2222 let mut blob2 = build_header_v2(1, 1, 1, 1, 0.0, 4.0, 4.0, 0);
2223 blob2.extend_from_slice(&0u32.to_le_bytes());
2224
2225 let mut merged = blob1;
2226 merged.extend_from_slice(&blob2);
2227 assert_eq!(get_band_count(&merged).unwrap(), 2);
2228 }
2229
2230 #[test]
2231 fn reads_blob_info_for_lerc1() {
2232 let blob = build_lerc1_blob(true, None, &[1.0, 2.0, 3.0, 4.0], 0.5, 4.0);
2233
2234 let info = get_blob_info(&blob).unwrap();
2235 assert_eq!(info.version, Version::Lerc1(11));
2236 assert_eq!(info.width, 2);
2237 assert_eq!(info.height, 2);
2238 assert_eq!(info.depth, 1);
2239 assert_eq!(info.data_type, DataType::F32);
2240 }
2241
2242 #[test]
2243 fn decodes_lerc1_masked_bitstuffed_block() {
2244 let blob = build_lerc1_blob(true, Some(&[1, 0, 1, 1]), &[1.0, 3.0, 4.0], 0.5, 4.0);
2245
2246 let decoded = decode(&blob).unwrap();
2247 assert_eq!(decoded.mask, Some(vec![1, 0, 1, 1]));
2248 assert_eq!(decoded.pixels, PixelData::F32(vec![1.0, 0.0, 3.0, 4.0]));
2249 assert_eq!(decoded.info.z_min, 1.0);
2250 assert_eq!(decoded.info.z_max, 4.0);
2251 }
2252
2253 #[test]
2254 fn counts_concatenated_lerc1_bands_with_shared_mask() {
2255 let blob1 = build_lerc1_blob(true, Some(&[1, 0, 1, 1]), &[1.0, 3.0, 4.0], 0.5, 4.0);
2256 let blob2 = build_lerc1_blob(false, None, &[1.0, 3.0, 4.0], 0.5, 4.0);
2257 let mut merged = blob1;
2258 merged.extend_from_slice(&blob2);
2259
2260 assert_eq!(get_band_count(&merged).unwrap(), 2);
2261 }
2262
2263 #[test]
2264 fn decodes_lerc2_to_ndarray() {
2265 let mut blob = build_header_v2(2, 2, 4, 1, 0.0, 1.0, 4.0, 1 + 4);
2266 blob.extend_from_slice(&0u32.to_le_bytes());
2267 blob.push(1);
2268 blob.extend_from_slice(&[1, 2, 3, 4]);
2269
2270 let array = decode_ndarray::<u8>(&blob).unwrap();
2271 assert_eq!(array.shape(), &[2, 2]);
2272 assert_eq!(array[IxDyn(&[0, 0])], 1);
2273 assert_eq!(array[IxDyn(&[0, 1])], 2);
2274 assert_eq!(array[IxDyn(&[1, 0])], 3);
2275 assert_eq!(array[IxDyn(&[1, 1])], 4);
2276 }
2277
2278 #[test]
2279 fn decodes_multidimensional_lerc2_to_f64_ndarray() {
2280 let mut bytes = Vec::new();
2281 bytes.extend_from_slice(MAGIC_LERC2);
2282 bytes.extend_from_slice(&4i32.to_le_bytes());
2283 bytes.extend_from_slice(&0u32.to_le_bytes());
2284 bytes.extend_from_slice(&1u32.to_le_bytes());
2285 bytes.extend_from_slice(&2u32.to_le_bytes());
2286 bytes.extend_from_slice(&2u32.to_le_bytes());
2287 bytes.extend_from_slice(&2u32.to_le_bytes());
2288 bytes.extend_from_slice(&8i32.to_le_bytes());
2289 bytes.extend_from_slice(&0i32.to_le_bytes());
2290 bytes.extend_from_slice(&6i32.to_le_bytes());
2291 bytes.extend_from_slice(&0.0f64.to_le_bytes());
2292 bytes.extend_from_slice(&10.0f64.to_le_bytes());
2293 bytes.extend_from_slice(&20.0f64.to_le_bytes());
2294 bytes.extend_from_slice(&0u32.to_le_bytes());
2295 bytes.extend_from_slice(&10.0f32.to_le_bytes());
2296 bytes.extend_from_slice(&20.0f32.to_le_bytes());
2297 bytes.extend_from_slice(&10.0f32.to_le_bytes());
2298 bytes.extend_from_slice(&20.0f32.to_le_bytes());
2299
2300 let blob = finalize_v4_with_checksum(bytes);
2301 let array = decode_ndarray_f64(&blob).unwrap();
2302 assert_eq!(array.shape(), &[1, 2, 2]);
2303 assert_eq!(array[IxDyn(&[0, 0, 0])], 10.0);
2304 assert_eq!(array[IxDyn(&[0, 0, 1])], 20.0);
2305 assert_eq!(array[IxDyn(&[0, 1, 0])], 10.0);
2306 assert_eq!(array[IxDyn(&[0, 1, 1])], 20.0);
2307 }
2308
2309 #[test]
2310 fn decodes_lerc1_mask_to_ndarray() {
2311 let blob = build_lerc1_blob(true, Some(&[1, 0, 1, 1]), &[1.0, 3.0, 4.0], 0.5, 4.0);
2312
2313 let mask = decode_mask_ndarray(&blob).unwrap().unwrap();
2314 assert_eq!(mask.shape(), &[2, 2]);
2315 assert_eq!(mask[IxDyn(&[0, 0])], 1);
2316 assert_eq!(mask[IxDyn(&[0, 1])], 0);
2317 assert_eq!(mask[IxDyn(&[1, 0])], 1);
2318 assert_eq!(mask[IxDyn(&[1, 1])], 1);
2319 }
2320}