use alloc::vec;
use alloc::vec::Vec;
use crate::bitmask::BitMask;
use crate::error::{LercError, Result};
use crate::rle;
use crate::types::TileRect;
use crate::{DataType, Image, LercInfo, SampleData};
const LERC1_MAGIC: &[u8; 10] = b"CntZImage ";
const LERC1_VERSION: i32 = 11;
const LERC1_TYPE: i32 = 8;
pub fn is_lerc1(data: &[u8]) -> bool {
data.len() >= 10 && &data[..10] == LERC1_MAGIC
}
struct Lerc1Header {
width: i32,
height: i32,
max_z_error: f64,
}
struct SectionHeader {
num_tiles_vert: i32,
num_tiles_hori: i32,
num_bytes: i32,
max_val_in_img: f32,
}
struct Cursor<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
fn check(&self, n: usize) -> Result<()> {
if self.remaining() < n {
Err(LercError::BufferTooSmall {
needed: self.pos + n,
available: self.data.len(),
})
} else {
Ok(())
}
}
fn read_u8(&mut self) -> Result<u8> {
self.check(1)?;
let v = self.data[self.pos];
self.pos += 1;
Ok(v)
}
fn read_i8(&mut self) -> Result<i8> {
self.check(1)?;
let v = self.data[self.pos] as i8;
self.pos += 1;
Ok(v)
}
fn read_i16_le(&mut self) -> Result<i16> {
self.check(2)?;
let v = i16::from_le_bytes(self.data[self.pos..self.pos + 2].try_into().unwrap());
self.pos += 2;
Ok(v)
}
fn read_i32_le(&mut self) -> Result<i32> {
self.check(4)?;
let v = i32::from_le_bytes(self.data[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
Ok(v)
}
fn read_u32_le(&mut self) -> Result<u32> {
self.check(4)?;
let v = u32::from_le_bytes(self.data[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
Ok(v)
}
fn read_f32_le(&mut self) -> Result<f32> {
self.check(4)?;
let v = f32::from_le_bytes(self.data[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
Ok(v)
}
fn read_f64_le(&mut self) -> Result<f64> {
self.check(8)?;
let v = f64::from_le_bytes(self.data[self.pos..self.pos + 8].try_into().unwrap());
self.pos += 8;
Ok(v)
}
fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
self.check(n)?;
let slice = &self.data[self.pos..self.pos + n];
self.pos += n;
Ok(slice)
}
}
fn read_flt(cursor: &mut Cursor, num_bytes: usize) -> Result<f32> {
match num_bytes {
1 => Ok(cursor.read_i8()? as f32),
2 => Ok(cursor.read_i16_le()? as f32),
4 => cursor.read_f32_le(),
_ => Err(LercError::InvalidData(
"lerc1: invalid float byte count".into(),
)),
}
}
fn read_uint(cursor: &mut Cursor, num_bytes: usize) -> Result<u32> {
match num_bytes {
1 => Ok(cursor.read_u8()? as u32),
2 => {
cursor.check(2)?;
let v = u16::from_le_bytes(cursor.data[cursor.pos..cursor.pos + 2].try_into().unwrap());
cursor.pos += 2;
Ok(v as u32)
}
4 => cursor.read_u32_le(),
_ => Err(LercError::InvalidData(
"lerc1: invalid uint byte count".into(),
)),
}
}
fn bit_unstuff(cursor: &mut Cursor) -> Result<Vec<u32>> {
let first_byte = cursor.read_u8()?;
let bits67 = (first_byte >> 6) as usize;
let n = if bits67 == 0 { 4 } else { 3 - bits67 };
let num_bits = (first_byte & 63) as u32;
let num_elements = read_uint(cursor, n)? as usize;
if num_bits >= 32 {
return Err(LercError::InvalidData(
"lerc1: numBits >= 32 in bit stuffer".into(),
));
}
let mut data_vec = vec![0u32; num_elements];
if num_bits > 0 {
let num_uints = (num_elements as u64 * num_bits as u64).div_ceil(32);
let n_bytes_to_copy = (num_elements as u64 * num_bits as u64).div_ceil(8) as usize;
cursor.check(n_bytes_to_copy)?;
let mut stuffed = vec![0u32; num_uints as usize];
for (i, word) in stuffed.iter_mut().enumerate() {
let base = cursor.pos + i * 4;
let mut bytes = [0u8; 4];
let end = (cursor.pos + n_bytes_to_copy).min(base + 4);
let count = end.saturating_sub(base);
bytes[..count].copy_from_slice(&cursor.data[base..base + count]);
*word = u32::from_le_bytes(bytes);
}
let num_bits_tail = (num_elements * num_bits as usize) & 31;
let num_bytes_tail = (num_bits_tail + 7) >> 3;
let tail_shift = if num_bytes_tail > 0 {
4 - num_bytes_tail
} else {
0
};
if let Some(last) = stuffed.last_mut() {
for _ in 0..tail_shift {
*last <<= 8;
}
}
let mut src_idx = 0usize;
let mut bit_pos = 0u32;
for dst in data_vec.iter_mut() {
if 32 - bit_pos >= num_bits {
let val = stuffed[src_idx];
let shifted = val << bit_pos;
*dst = shifted >> (32 - num_bits);
bit_pos += num_bits;
if bit_pos == 32 {
bit_pos = 0;
src_idx += 1;
}
} else {
let val = stuffed[src_idx];
src_idx += 1;
let shifted = val << bit_pos;
*dst = shifted >> (32 - num_bits);
bit_pos -= 32 - num_bits;
let val2 = stuffed[src_idx];
*dst |= val2 >> (32 - bit_pos);
}
}
cursor.pos += n_bytes_to_copy;
}
Ok(data_vec)
}
fn read_lerc1_header(cursor: &mut Cursor) -> Result<Lerc1Header> {
let magic = cursor.read_bytes(10)?;
if magic != LERC1_MAGIC {
return Err(LercError::InvalidMagic);
}
let version = cursor.read_i32_le()?;
let type_id = cursor.read_i32_le()?;
let height = cursor.read_i32_le()?;
let width = cursor.read_i32_le()?;
let max_z_error = cursor.read_f64_le()?;
if version != LERC1_VERSION {
return Err(LercError::UnsupportedVersion(version));
}
if type_id != LERC1_TYPE {
return Err(LercError::InvalidData(alloc::format!(
"lerc1: unexpected type {type_id}, expected {LERC1_TYPE}"
)));
}
if width <= 0 || height <= 0 || width > 20000 || height > 20000 {
return Err(LercError::InvalidData(alloc::format!(
"lerc1: invalid dimensions {width}x{height}"
)));
}
Ok(Lerc1Header {
width,
height,
max_z_error,
})
}
fn read_section_header(cursor: &mut Cursor) -> Result<SectionHeader> {
let num_tiles_vert = cursor.read_i32_le()?;
let num_tiles_hori = cursor.read_i32_le()?;
let num_bytes = cursor.read_i32_le()?;
let max_val_in_img = cursor.read_f32_le()?;
Ok(SectionHeader {
num_tiles_vert,
num_tiles_hori,
num_bytes,
max_val_in_img,
})
}
fn read_cnt_tile(
cursor: &mut Cursor,
cnt: &mut [f32],
width: usize,
i0: usize,
i1: usize,
j0: usize,
j1: usize,
) -> Result<()> {
let compr_flag = cursor.read_u8()?;
if compr_flag == 2 {
return Ok(());
}
if compr_flag == 3 || compr_flag == 4 {
let val = if compr_flag == 3 { -1.0f32 } else { 1.0f32 };
for i in i0..i1 {
for j in j0..j1 {
cnt[i * width + j] = val;
}
}
return Ok(());
}
if (compr_flag & 63) > 4 {
return Err(LercError::InvalidData(
"lerc1: invalid cnt tile compression flag".into(),
));
}
if compr_flag == 0 {
for i in i0..i1 {
for j in j0..j1 {
cnt[i * width + j] = cursor.read_f32_le()?;
}
}
} else {
let bits67 = (compr_flag >> 6) as usize;
let n = if bits67 == 0 { 4 } else { 3 - bits67 };
let offset = read_flt(cursor, n)?;
let data_vec = bit_unstuff(cursor)?;
let num_pixel = (i1 - i0) * (j1 - j0);
if data_vec.len() < num_pixel {
return Err(LercError::InvalidData(
"lerc1: bit stuffer returned too few elements for cnt tile".into(),
));
}
let mut src_idx = 0;
for i in i0..i1 {
for j in j0..j1 {
cnt[i * width + j] = offset + data_vec[src_idx] as f32;
src_idx += 1;
}
}
}
Ok(())
}
struct Lerc1ZConfig {
width: usize,
height: usize,
max_z_error_in_file: f64,
max_z_in_img: f32,
decoder_can_ignore_mask: bool,
}
fn read_z_tile(
cursor: &mut Cursor,
cnt: &[f32],
z: &mut [f32],
cfg: &Lerc1ZConfig,
rect: TileRect,
) -> Result<()> {
let TileRect { i0, i1, j0, j1 } = rect;
let width = cfg.width;
let byte0 = cursor.read_u8()?;
let bits67 = (byte0 >> 6) as usize;
let compr_flag = byte0 & 63;
if compr_flag == 2 {
for i in i0..i1 {
for j in j0..j1 {
let idx = i * width + j;
if cnt[idx] > 0.0 {
z[idx] = 0.0;
}
}
}
return Ok(());
}
if compr_flag > 3 {
return Err(LercError::InvalidData(
"lerc1: invalid z tile compression flag".into(),
));
}
if compr_flag == 0 {
for i in i0..i1 {
for j in j0..j1 {
let idx = i * width + j;
if cnt[idx] > 0.0 {
z[idx] = cursor.read_f32_le()?;
}
}
}
} else {
let n = if bits67 == 0 { 4 } else { 3 - bits67 };
let offset = read_flt(cursor, n)?;
if compr_flag == 3 {
for i in i0..i1 {
for j in j0..j1 {
let idx = i * width + j;
if cnt[idx] > 0.0 {
z[idx] = offset;
}
}
}
} else {
let data_vec = bit_unstuff(cursor)?;
let inv_scale = 2.0 * cfg.max_z_error_in_file;
let mut src_idx = 0;
if cfg.decoder_can_ignore_mask {
for i in i0..i1 {
for j in j0..j1 {
let idx = i * width + j;
let val = (offset as f64 + data_vec[src_idx] as f64 * inv_scale) as f32;
z[idx] = val.min(cfg.max_z_in_img);
src_idx += 1;
}
}
} else {
for i in i0..i1 {
for j in j0..j1 {
let idx = i * width + j;
if cnt[idx] > 0.0 {
let val = (offset as f64 + data_vec[src_idx] as f64 * inv_scale) as f32;
z[idx] = val.min(cfg.max_z_in_img);
src_idx += 1;
}
}
}
}
}
}
Ok(())
}
fn read_tiles_cnt(
cursor: &mut Cursor,
cnt: &mut [f32],
width: usize,
height: usize,
num_tiles_vert: i32,
num_tiles_hori: i32,
) -> Result<()> {
if num_tiles_vert <= 0 || num_tiles_hori <= 0 {
return Err(LercError::InvalidData("lerc1: invalid tile counts".into()));
}
for i_tile in 0..=num_tiles_vert {
let tile_h = if i_tile == num_tiles_vert {
height % num_tiles_vert as usize
} else {
height / num_tiles_vert as usize
};
if tile_h == 0 {
continue;
}
let i0 = i_tile as usize * (height / num_tiles_vert as usize);
for j_tile in 0..=num_tiles_hori {
let tile_w = if j_tile == num_tiles_hori {
width % num_tiles_hori as usize
} else {
width / num_tiles_hori as usize
};
if tile_w == 0 {
continue;
}
let j0 = j_tile as usize * (width / num_tiles_hori as usize);
read_cnt_tile(cursor, cnt, width, i0, i0 + tile_h, j0, j0 + tile_w)?;
}
}
Ok(())
}
fn read_tiles_z(
cursor: &mut Cursor,
cnt: &[f32],
z: &mut [f32],
cfg: &Lerc1ZConfig,
num_tiles_vert: i32,
num_tiles_hori: i32,
) -> Result<()> {
let width = cfg.width;
let height = cfg.height;
if num_tiles_vert <= 0 || num_tiles_hori <= 0 {
return Err(LercError::InvalidData("lerc1: invalid tile counts".into()));
}
for i_tile in 0..=num_tiles_vert {
let tile_h = if i_tile == num_tiles_vert {
height % num_tiles_vert as usize
} else {
height / num_tiles_vert as usize
};
if tile_h == 0 {
continue;
}
let i0 = i_tile as usize * (height / num_tiles_vert as usize);
for j_tile in 0..=num_tiles_hori {
let tile_w = if j_tile == num_tiles_hori {
width % num_tiles_hori as usize
} else {
width / num_tiles_hori as usize
};
if tile_w == 0 {
continue;
}
let j0 = j_tile as usize * (width / num_tiles_hori as usize);
let rect = TileRect {
i0,
i1: i0 + tile_h,
j0,
j1: j0 + tile_w,
};
read_z_tile(cursor, cnt, z, cfg, rect)?;
}
}
Ok(())
}
pub fn decode_info(data: &[u8]) -> Result<LercInfo> {
let mut cursor = Cursor::new(data);
let hd = read_lerc1_header(&mut cursor)?;
let cnt_sec = read_section_header(&mut cursor)?;
if cnt_sec.num_bytes < 0 || cursor.pos + cnt_sec.num_bytes as usize > data.len() {
return Err(LercError::BufferTooSmall {
needed: cursor.pos + cnt_sec.num_bytes.max(0) as usize,
available: data.len(),
});
}
cursor.pos += cnt_sec.num_bytes as usize;
let z_sec = read_section_header(&mut cursor)?;
let blob_size = cursor.pos + z_sec.num_bytes.max(0) as usize;
Ok(LercInfo {
version: LERC1_VERSION,
width: hd.width as u32,
height: hd.height as u32,
depth: 1,
bands: 1,
data_type: DataType::Float,
valid_pixels: (hd.width as u32) * (hd.height as u32),
tolerance: hd.max_z_error,
min_value: f64::NAN, max_value: z_sec.max_val_in_img as f64,
blob_size: blob_size as u32,
..Default::default()
})
}
pub fn decode(data: &[u8]) -> Result<Image> {
let mut cursor = Cursor::new(data);
let hd = read_lerc1_header(&mut cursor)?;
let width = hd.width as usize;
let height = hd.height as usize;
let num_pixels = width * height;
let mut cnt = vec![0.0f32; num_pixels];
let mut z = vec![0.0f32; num_pixels];
let mut decoder_can_ignore_mask = false;
let cnt_sec = read_section_header(&mut cursor)?;
if cnt_sec.num_bytes < 0 {
return Err(LercError::InvalidData(
"lerc1: negative numBytes in count section".into(),
));
}
let cnt_data_start = cursor.pos;
if cnt_sec.num_tiles_vert == 0 && cnt_sec.num_tiles_hori == 0 {
if cnt_sec.num_bytes == 0 {
for c in cnt.iter_mut() {
*c = cnt_sec.max_val_in_img;
}
if cnt_sec.max_val_in_img > 0.0 {
decoder_can_ignore_mask = true;
}
} else {
let mask_byte_count = num_pixels.div_ceil(8);
let rle_data = &data[cursor.pos..cursor.pos + cnt_sec.num_bytes as usize];
let mask_bytes = rle::decompress(rle_data, mask_byte_count)?;
let bitmask = BitMask::from_bytes(mask_bytes, num_pixels);
for (k, c) in cnt[..num_pixels].iter_mut().enumerate() {
*c = if bitmask.is_valid(k) { 1.0 } else { 0.0 };
}
}
} else {
read_tiles_cnt(
&mut cursor,
&mut cnt,
width,
height,
cnt_sec.num_tiles_vert,
cnt_sec.num_tiles_hori,
)?;
}
cursor.pos = cnt_data_start + cnt_sec.num_bytes as usize;
let z_sec = read_section_header(&mut cursor)?;
if z_sec.num_bytes < 0 {
return Err(LercError::InvalidData(
"lerc1: negative numBytes in z section".into(),
));
}
let z_cfg = Lerc1ZConfig {
width,
height,
max_z_error_in_file: hd.max_z_error,
max_z_in_img: z_sec.max_val_in_img,
decoder_can_ignore_mask,
};
read_tiles_z(
&mut cursor,
&cnt,
&mut z,
&z_cfg,
z_sec.num_tiles_vert,
z_sec.num_tiles_hori,
)?;
let mut mask = BitMask::new(num_pixels);
for (k, &c) in cnt[..num_pixels].iter().enumerate() {
if c > 0.0 {
mask.set_valid(k);
}
}
Ok(Image {
width: width as u32,
height: height as u32,
depth: 1,
bands: 1,
data_type: DataType::Float,
valid_masks: vec![mask],
data: SampleData::F32(z),
..Default::default()
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bit_unstuff_zero_bits() {
let data = [0x40, 0x05, 0x00];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert_eq!(result, vec![0u32; 5]);
}
#[test]
fn bit_unstuff_1bit_values() {
let data = [0x41, 0x04, 0x00, 0xB0];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert_eq!(result, vec![1, 0, 1, 1]);
}
#[test]
fn bit_unstuff_empty_elements() {
let data = [0x41, 0x00, 0x00];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert!(result.is_empty());
}
#[test]
fn bit_unstuff_31bit_values() {
let data = [0x5F, 0x01, 0x00, 0xFE, 0xFF, 0xFF, 0xFF];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert_eq!(result, vec![0x7FFF_FFFF]);
}
#[test]
fn bit_unstuff_multi_word_3bit() {
let data = [0x42, 0x03, 0x00, 0x6C];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn bit_unstuff_8bit_values() {
let data = [0x48, 0x03, 0x00, 0x2A, 0xC8, 0x64];
let mut cursor = Cursor::new(&data);
let result = bit_unstuff(&mut cursor).unwrap();
assert_eq!(result, vec![100, 200, 42]);
}
#[test]
fn bit_unstuff_rejects_32bit() {
let data = [0x60, 0x01, 0x00];
let mut cursor = Cursor::new(&data);
assert!(bit_unstuff(&mut cursor).is_err());
}
#[test]
fn read_flt_1byte() {
let data = [0xFE]; let mut cursor = Cursor::new(&data);
let val = read_flt(&mut cursor, 1).unwrap();
assert_eq!(val, -2.0);
}
#[test]
fn read_flt_2bytes() {
let data = 300i16.to_le_bytes();
let mut cursor = Cursor::new(&data);
let val = read_flt(&mut cursor, 2).unwrap();
assert_eq!(val, 300.0);
}
#[test]
fn read_flt_4bytes() {
let data = 1.5f32.to_le_bytes();
let mut cursor = Cursor::new(&data);
let val = read_flt(&mut cursor, 4).unwrap();
assert_eq!(val, 1.5);
}
#[test]
fn read_flt_invalid_size() {
let data = [0u8; 8];
let mut cursor = Cursor::new(&data);
assert!(read_flt(&mut cursor, 3).is_err());
assert!(read_flt(&mut cursor, 0).is_err());
}
#[test]
fn read_f64_le_known_value() {
let val = core::f64::consts::PI;
let data = val.to_le_bytes();
let mut cursor = Cursor::new(&data);
let result = cursor.read_f64_le().unwrap();
assert_eq!(result, val);
}
#[test]
fn read_f64_le_negative() {
let val: f64 = -123456.789;
let data = val.to_le_bytes();
let mut cursor = Cursor::new(&data);
let result = cursor.read_f64_le().unwrap();
assert_eq!(result, val);
}
#[test]
fn read_f64_le_zero() {
let data = 0.0f64.to_le_bytes();
let mut cursor = Cursor::new(&data);
let result = cursor.read_f64_le().unwrap();
assert_eq!(result, 0.0);
}
#[test]
fn read_f64_le_insufficient_data() {
let data = [0u8; 4]; let mut cursor = Cursor::new(&data);
assert!(cursor.read_f64_le().is_err());
}
#[test]
fn is_lerc1_detection() {
assert!(is_lerc1(b"CntZImage extra data"));
assert!(!is_lerc1(b"Lerc2 data"));
assert!(!is_lerc1(b"short"));
}
#[test]
fn cursor_remaining_and_check() {
let data = [1u8, 2, 3, 4];
let mut cursor = Cursor::new(&data);
assert_eq!(cursor.remaining(), 4);
cursor.pos = 2;
assert_eq!(cursor.remaining(), 2);
assert!(cursor.check(2).is_ok());
assert!(cursor.check(3).is_err());
}
#[test]
fn read_uint_1byte() {
let data = [0xFF];
let mut cursor = Cursor::new(&data);
assert_eq!(read_uint(&mut cursor, 1).unwrap(), 255);
}
#[test]
fn read_uint_2bytes() {
let data = 0x1234u16.to_le_bytes();
let mut cursor = Cursor::new(&data);
assert_eq!(read_uint(&mut cursor, 2).unwrap(), 0x1234);
}
#[test]
fn read_uint_4bytes() {
let data = 0xDEAD_BEEFu32.to_le_bytes();
let mut cursor = Cursor::new(&data);
assert_eq!(read_uint(&mut cursor, 4).unwrap(), 0xDEAD_BEEF);
}
#[test]
fn read_uint_invalid_size() {
let data = [0u8; 8];
let mut cursor = Cursor::new(&data);
assert!(read_uint(&mut cursor, 3).is_err());
}
fn make_z_tile_type2() -> Vec<u8> {
vec![0x02]
}
fn make_z_tile_type3(offset: f32) -> Vec<u8> {
let mut buf = vec![0x03];
buf.extend_from_slice(&offset.to_le_bytes());
buf
}
fn make_z_tile_type0(values: &[f32]) -> Vec<u8> {
let mut buf = vec![0x00];
for &v in values {
buf.extend_from_slice(&v.to_le_bytes());
}
buf
}
#[test]
fn decode_z_tile_type2_constant_zero() {
let tile_data = make_z_tile_type2();
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0f32; 4]; let mut z = vec![f32::NAN; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z, vec![0.0, 0.0, 0.0, 0.0]);
}
#[test]
fn decode_z_tile_type2_skips_invalid() {
let tile_data = make_z_tile_type2();
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0, 0.0, 0.0, 1.0]; let mut z = vec![f32::NAN; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z[0], 0.0);
assert!(z[1].is_nan()); assert!(z[2].is_nan());
assert_eq!(z[3], 0.0);
}
#[test]
fn decode_z_tile_type3_constant_value() {
let tile_data = make_z_tile_type3(42.5);
let mut cursor = Cursor::new(&tile_data);
let width = 3;
let cnt = vec![1.0f32; 6];
let mut z = vec![0.0f32; 6];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.5,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 3,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
for &val in &z {
assert_eq!(val, 42.5);
}
}
#[test]
fn decode_z_tile_type3_respects_mask() {
let tile_data = make_z_tile_type3(7.0);
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0, 0.0, 0.0, 1.0];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z[0], 7.0);
assert_eq!(z[1], 0.0); assert_eq!(z[2], 0.0); assert_eq!(z[3], 7.0);
}
#[test]
fn decode_z_tile_type0_uncompressed() {
let values = [1.0f32, 2.0, 3.0, 4.0];
let tile_data = make_z_tile_type0(&values);
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0f32; 4];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z, vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn decode_z_tile_type0_uncompressed_with_mask() {
let values = [10.0f32, 20.0];
let tile_data = make_z_tile_type0(&values);
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0, 0.0, 0.0, 1.0];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z[0], 10.0);
assert_eq!(z[3], 20.0);
}
#[test]
fn decode_z_tile_invalid_compr_flag() {
let tile_data = [0x05u8];
let mut cursor = Cursor::new(&tile_data);
let width = 2;
let cnt = vec![1.0f32; 4];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.0,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
let result = read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect);
assert!(result.is_err());
}
#[test]
fn decode_z_tile_type1_bit_stuffed() {
let mut buf = vec![0x01u8];
buf.extend_from_slice(&10.0f32.to_le_bytes());
buf.push(0x42); buf.extend_from_slice(&4u16.to_le_bytes()); buf.push(0x1B);
let mut cursor = Cursor::new(&buf);
let width = 2;
let cnt = vec![1.0f32; 4];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.5,
max_z_in_img: 100.0,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z, vec![10.0, 11.0, 12.0, 13.0]);
}
#[test]
fn decode_z_tile_type1_clamped_to_max() {
let mut buf = vec![0x01u8];
buf.extend_from_slice(&10.0f32.to_le_bytes());
buf.push(0x42); buf.extend_from_slice(&4u16.to_le_bytes());
buf.push(0x1B);
let mut cursor = Cursor::new(&buf);
let width = 2;
let cnt = vec![1.0f32; 4];
let mut z = vec![0.0f32; 4];
let cfg = Lerc1ZConfig {
width,
height: 2,
max_z_error_in_file: 0.5,
max_z_in_img: 11.5,
decoder_can_ignore_mask: false,
};
let rect = TileRect {
i0: 0,
i1: 2,
j0: 0,
j1: 2,
};
read_z_tile(&mut cursor, &cnt, &mut z, &cfg, rect).unwrap();
assert_eq!(z[0], 10.0);
assert_eq!(z[1], 11.0);
assert_eq!(z[2], 11.5); assert_eq!(z[3], 11.5); }
}