use crate::las::utils::flag_diff;
use crate::packers::Packable;
pub trait LasRGB {
fn red(&self) -> u16;
fn green(&self) -> u16;
fn blue(&self) -> u16;
fn set_red(&mut self, new_val: u16);
fn set_green(&mut self, new_val: u16);
fn set_blue(&mut self, new_val: u16);
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
pub struct RGB {
pub red: u16,
pub green: u16,
pub blue: u16,
}
impl RGB {
pub const SIZE: usize = 6;
}
impl LasRGB for RGB {
fn red(&self) -> u16 {
self.red
}
fn green(&self) -> u16 {
self.green
}
fn blue(&self) -> u16 {
self.blue
}
fn set_red(&mut self, new_val: u16) {
self.red = new_val;
}
fn set_green(&mut self, new_val: u16) {
self.green = new_val
}
fn set_blue(&mut self, new_val: u16) {
self.blue = new_val;
}
}
struct ColorDiff(u8);
impl ColorDiff {
fn from_points<P: LasRGB, OP: LasRGB>(current: &P, last: &OP) -> Self {
let v = (flag_diff(last.red(), current.red(), 0x00FF) as u8) << 0
| (flag_diff(last.red(), current.red(), 0xFF00) as u8) << 1
| (flag_diff(last.green(), current.green(), 0x00FF) as u8) << 2
| (flag_diff(last.green(), current.green(), 0xFF00) as u8) << 3
| (flag_diff(last.blue(), current.blue(), 0x00FF) as u8) << 4
| (flag_diff(last.blue(), current.blue(), 0xFF00) as u8) << 5
| ((flag_diff(current.red(), current.green(), 0x00FF)
|| flag_diff(current.red(), current.blue(), 0x00FF)
|| flag_diff(current.red(), current.green(), 0xFF00)
|| flag_diff(current.red(), current.blue(), 0xFF00)) as u8)
<< 6;
Self { 0: v }
}
fn new(v: u8) -> Self {
Self { 0: v }
}
fn lower_red_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 0)
}
fn upper_red_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 1)
}
fn lower_green_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 2)
}
fn upper_green_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 3)
}
fn lower_blue_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 4)
}
fn upper_blue_byte_changed(&self) -> bool {
is_nth_bit_set!(self.0, 5)
}
}
impl Packable for RGB {
fn unpack_from(input: &[u8]) -> Self {
assert!(input.len() >= 6);
unsafe { Self::unpack_from_unchecked(input) }
}
fn pack_into(&self, output: &mut [u8]) {
assert!(output.len() >= 6);
unsafe { self.pack_into_unchecked(output) }
}
unsafe fn unpack_from_unchecked(input: &[u8]) -> Self {
debug_assert!(input.len() >= 6);
Self {
red: u16::unpack_from_unchecked(input.get_unchecked(0..2)),
green: u16::unpack_from_unchecked(&input.get_unchecked(2..4)),
blue: u16::unpack_from_unchecked(&input.get_unchecked(4..6)),
}
}
unsafe fn pack_into_unchecked(&self, output: &mut [u8]) {
debug_assert!(output.len() >= 6);
u16::pack_into_unchecked(&self.red, output.get_unchecked_mut(0..2));
u16::pack_into_unchecked(&self.green, output.get_unchecked_mut(2..4));
u16::pack_into_unchecked(&self.blue, output.get_unchecked_mut(4..6));
}
}
pub mod v1 {
use std::io::{Read, Write};
use std::mem::size_of;
use crate::compressors::{IntegerCompressor, IntegerCompressorBuilder};
use crate::decoders::ArithmeticDecoder;
use crate::decompressors::{IntegerDecompressor, IntegerDecompressorBuilder};
use crate::encoders::ArithmeticEncoder;
use crate::las::rgb::LasRGB;
use crate::las::utils::{lower_byte, read_and_unpack, upper_byte};
use crate::models::{ArithmeticModel, ArithmeticModelBuilder};
use crate::packers::Packable;
use crate::record::{FieldCompressor, FieldDecompressor};
use super::{ColorDiff, RGB};
const LOWER_RED_BYTE_CONTEXT: u32 = 0;
const UPPER_RED_BYTE_CONTEXT: u32 = 1;
const LOWER_GREEN_BYTE_CONTEXT: u32 = 2;
const UPPER_GREEN_BYTE_CONTEXT: u32 = 3;
const LOWER_BLUE_BYTE_CONTEXT: u32 = 4;
const UPPER_BLUE_BYTE_CONTEXT: u32 = 5;
pub struct LasRGBDecompressor {
last: RGB,
byte_used_model: ArithmeticModel,
decompressor: IntegerDecompressor,
}
impl Default for LasRGBDecompressor {
fn default() -> Self {
Self {
last: Default::default(),
byte_used_model: ArithmeticModelBuilder::new(64).build(),
decompressor: IntegerDecompressorBuilder::new()
.bits(8)
.contexts(6)
.build_initialized(),
}
}
}
impl LasRGBDecompressor {
pub fn decompress_byte<R: Read>(
&mut self,
decoder: &mut ArithmeticDecoder<R>,
context: u32,
last_byte_value: u8,
) -> std::io::Result<i32> {
self.decompressor
.decompress(decoder, i32::from(last_byte_value), context)
}
}
pub struct LasRGBCompressor {
last: RGB,
byte_used_model: ArithmeticModel,
compressor: IntegerCompressor,
}
impl Default for LasRGBCompressor {
fn default() -> Self {
Self {
last: Default::default(),
byte_used_model: ArithmeticModelBuilder::new(64).build(),
compressor: IntegerCompressorBuilder::new()
.bits(8)
.contexts(6)
.build_initialized(),
}
}
}
impl<R: Read> FieldDecompressor<R> for LasRGBDecompressor {
fn size_of_field(&self) -> usize {
3 * size_of::<u16>()
}
fn decompress_first(&mut self, src: &mut R, first_point: &mut [u8]) -> std::io::Result<()> {
self.last = read_and_unpack::<_, RGB>(src, first_point)?;
Ok(())
}
fn decompress_with(
&mut self,
decoder: &mut ArithmeticDecoder<R>,
buf: &mut [u8],
) -> std::io::Result<()> {
let color_diff =
ColorDiff::new(decoder.decode_symbol(&mut self.byte_used_model)? as u8);
if color_diff.lower_red_byte_changed() {
let new_lower_red = self.decompress_byte(
decoder,
LOWER_RED_BYTE_CONTEXT,
lower_byte(self.last.red),
)?;
self.last.red = new_lower_red as u16 | self.last.red & 0xFF00
}
if color_diff.upper_red_byte_changed() {
self.last.red |= (self.decompress_byte(
decoder,
UPPER_RED_BYTE_CONTEXT,
upper_byte(self.last.red),
)? as u16)
<< 8;
}
if color_diff.lower_green_byte_changed() {
let new_lower_green = self.decompress_byte(
decoder,
LOWER_GREEN_BYTE_CONTEXT,
lower_byte(self.last.green),
)?;
self.last.green = new_lower_green as u16 | self.last.green & 0xFF00;
}
if color_diff.upper_green_byte_changed() {
self.last.green |= (self.decompress_byte(
decoder,
UPPER_GREEN_BYTE_CONTEXT,
upper_byte(self.last.green),
)? as u16)
<< 8;
}
if color_diff.lower_blue_byte_changed() {
let new_lower_blue = self.decompress_byte(
decoder,
LOWER_BLUE_BYTE_CONTEXT,
lower_byte(self.last.blue),
)?;
self.last.blue = new_lower_blue as u16 | self.last.blue & 0xFF00;
}
if color_diff.upper_blue_byte_changed() {
self.last.blue |= (self.decompress_byte(
decoder,
UPPER_BLUE_BYTE_CONTEXT,
upper_byte(self.last.blue),
)? as u16)
<< 8;
}
self.last.pack_into(buf);
Ok(())
}
}
impl<W: Write> FieldCompressor<W> for LasRGBCompressor {
fn size_of_field(&self) -> usize {
6
}
fn compress_first(&mut self, dst: &mut W, buf: &[u8]) -> std::io::Result<()> {
self.last = RGB::unpack_from(buf);
dst.write_all(buf)
}
fn compress_with(
&mut self,
mut encoder: &mut ArithmeticEncoder<W>,
buf: &[u8],
) -> std::io::Result<()> {
let current_point = RGB::unpack_from(buf);
let sym = ((lower_byte(self.last.red()) != lower_byte(current_point.red())) as u8) << 0
| ((upper_byte(self.last.red()) != upper_byte(current_point.red())) as u8) << 1
| ((lower_byte(self.last.green()) != lower_byte(current_point.green())) as u8) << 2
| ((upper_byte(self.last.green()) != upper_byte(current_point.green())) as u8) << 3
| ((lower_byte(self.last.blue()) != lower_byte(current_point.blue())) as u8) << 4
| ((upper_byte(self.last.blue()) != upper_byte(current_point.blue())) as u8) << 5;
encoder.encode_symbol(&mut self.byte_used_model, u32::from(sym))?;
let color_diff = ColorDiff::new(sym);
if color_diff.lower_red_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(lower_byte(self.last.red)),
i32::from(lower_byte(current_point.red)),
LOWER_RED_BYTE_CONTEXT,
)?;
}
if color_diff.upper_red_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(upper_byte(self.last.red)),
i32::from(upper_byte(current_point.red)),
UPPER_RED_BYTE_CONTEXT,
)?;
}
if color_diff.lower_green_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(lower_byte(self.last.green)),
i32::from(lower_byte(current_point.green)),
LOWER_GREEN_BYTE_CONTEXT,
)?;
}
if color_diff.upper_green_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(upper_byte(self.last.green)),
i32::from(upper_byte(current_point.green)),
UPPER_GREEN_BYTE_CONTEXT,
)?;
}
if color_diff.lower_blue_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(lower_byte(self.last.blue)),
i32::from(lower_byte(current_point.blue)),
LOWER_BLUE_BYTE_CONTEXT,
)?;
}
if color_diff.upper_blue_byte_changed() {
self.compressor.compress(
&mut encoder,
i32::from(upper_byte(self.last.blue)),
i32::from(upper_byte(current_point.blue)),
UPPER_GREEN_BYTE_CONTEXT,
)?;
}
self.last = current_point;
Ok(())
}
}
}
pub mod v2 {
use std::io::{Read, Write};
use crate::decoders::ArithmeticDecoder;
use crate::encoders::ArithmeticEncoder;
use crate::las::rgb::LasRGB;
use crate::las::utils::{lower_byte, read_and_unpack, u8_clamp, upper_byte};
use crate::models::{ArithmeticModel, ArithmeticModelBuilder};
use crate::packers::Packable;
use crate::record::{FieldCompressor, FieldDecompressor};
use super::{ColorDiff, RGB};
pub(crate) struct RGBModels {
byte_used: ArithmeticModel,
lower_red_byte: ArithmeticModel,
upper_red_byte: ArithmeticModel,
lower_green_byte: ArithmeticModel,
upper_green_byte: ArithmeticModel,
lower_blue_byte: ArithmeticModel,
upper_blue_byte: ArithmeticModel,
}
impl Default for RGBModels {
fn default() -> Self {
Self {
byte_used: ArithmeticModelBuilder::new(128).build(),
lower_red_byte: ArithmeticModelBuilder::new(256).build(),
upper_red_byte: ArithmeticModelBuilder::new(256).build(),
lower_green_byte: ArithmeticModelBuilder::new(256).build(),
upper_green_byte: ArithmeticModelBuilder::new(256).build(),
lower_blue_byte: ArithmeticModelBuilder::new(256).build(),
upper_blue_byte: ArithmeticModelBuilder::new(256).build(),
}
}
}
pub(crate) fn compress_rgb_using<W: Write>(
encoder: &mut ArithmeticEncoder<W>,
models: &mut RGBModels,
current_rgb: &RGB,
last_rgb: &RGB,
) -> std::io::Result<()> {
let mut diff_l = 0i32;
let mut diff_h = 0i32;
let mut corr;
let color_diff = ColorDiff::from_points(current_rgb, last_rgb);
encoder.encode_symbol(&mut models.byte_used, u32::from(color_diff.0))?;
if color_diff.lower_red_byte_changed() {
diff_l = i32::from(lower_byte(current_rgb.red)) - i32::from(lower_byte(last_rgb.red));
encoder.encode_symbol(&mut models.lower_red_byte, u32::from(diff_l as u8))?;
}
if color_diff.upper_red_byte_changed() {
diff_h = i32::from(upper_byte(current_rgb.red)) - i32::from(upper_byte(last_rgb.red));
encoder.encode_symbol(&mut models.upper_red_byte, u32::from(diff_h as u8))?;
}
if (color_diff.0 & (1 << 6)) != 0 {
if color_diff.lower_green_byte_changed() {
corr = i32::from(lower_byte(current_rgb.green))
- i32::from(u8_clamp(diff_l + i32::from(lower_byte(last_rgb.green))));
encoder.encode_symbol(&mut models.lower_green_byte, u32::from(corr as u8))?;
}
if color_diff.lower_blue_byte_changed() {
diff_l = (diff_l + i32::from(lower_byte(current_rgb.green()))
- i32::from(lower_byte(last_rgb.green)))
/ 2;
corr = i32::from(lower_byte(current_rgb.blue))
- i32::from(u8_clamp(diff_l + i32::from(lower_byte(last_rgb.blue))));
encoder.encode_symbol(&mut models.lower_blue_byte, u32::from(corr as u8))?;
}
if color_diff.upper_green_byte_changed() {
corr = i32::from(upper_byte(current_rgb.green))
- i32::from(u8_clamp(diff_h + i32::from(upper_byte(last_rgb.green))));
encoder.encode_symbol(&mut models.upper_green_byte, u32::from(corr as u8))?;
}
if color_diff.upper_blue_byte_changed() {
diff_h = (diff_h + i32::from(upper_byte(current_rgb.green))
- i32::from(upper_byte(last_rgb.green)))
/ 2;
corr = i32::from(upper_byte(current_rgb.blue))
- i32::from(u8_clamp(diff_h + i32::from(upper_byte(last_rgb.blue))));
encoder.encode_symbol(&mut models.upper_blue_byte, u32::from(corr as u8))?;
}
}
Ok(())
}
pub struct LasRGBCompressor {
last: RGB,
models: RGBModels,
}
impl Default for LasRGBCompressor {
fn default() -> Self {
Self {
last: RGB::default(),
models: RGBModels::default(),
}
}
}
impl<W: Write> FieldCompressor<W> for LasRGBCompressor {
fn size_of_field(&self) -> usize {
3 * std::mem::size_of::<u16>()
}
fn compress_first(&mut self, dst: &mut W, buf: &[u8]) -> std::io::Result<()> {
self.last = super::RGB::unpack_from(&buf);
dst.write_all(buf)
}
fn compress_with(
&mut self,
encoder: &mut ArithmeticEncoder<W>,
buf: &[u8],
) -> std::io::Result<()> {
let current_point = super::RGB::unpack_from(&buf);
compress_rgb_using(encoder, &mut self.models, ¤t_point, &self.last)?;
self.last = current_point;
Ok(())
}
}
pub struct LasRGBDecompressor {
last: RGB,
models: RGBModels,
}
impl Default for LasRGBDecompressor {
fn default() -> Self {
Self {
last: RGB::default(),
models: RGBModels::default(),
}
}
}
pub(crate) fn decompress_rgb_using<R: Read>(
decoder: &mut ArithmeticDecoder<R>,
models: &mut RGBModels,
last: &RGB,
) -> std::io::Result<RGB> {
let sym = decoder.decode_symbol(&mut models.byte_used)?;
let color_diff = ColorDiff { 0: sym as u8 };
let mut this_val = RGB::default();
let mut corr;
let mut diff;
if color_diff.lower_red_byte_changed() {
corr = decoder.decode_symbol(&mut models.lower_red_byte)? as u8;
this_val.red = u16::from(corr.wrapping_add(lower_byte(last.red)));
} else {
this_val.red = last.red & 0xFF;
}
if color_diff.upper_red_byte_changed() {
corr = decoder.decode_symbol(&mut models.upper_red_byte)? as u8;
this_val.red |= u16::from(corr.wrapping_add(upper_byte(last.red))) << 8;
} else {
this_val.red |= last.red & 0xFF00;
}
if (sym & (1 << 6)) != 0 {
diff = i32::from(lower_byte(this_val.red)) - i32::from(lower_byte(last.red));
if color_diff.lower_green_byte_changed() {
corr = decoder.decode_symbol(&mut models.lower_green_byte)? as u8;
this_val.green = u16::from(
corr.wrapping_add(u8_clamp(diff + i32::from(lower_byte(last.green)))),
);
} else {
this_val.green = last.green & 0x00FF;
}
if color_diff.lower_blue_byte_changed() {
corr = decoder.decode_symbol(&mut models.lower_blue_byte)? as u8;
diff = (diff + i32::from(lower_byte(this_val.green))
- i32::from(lower_byte(last.green)))
/ 2;
this_val.blue =
u16::from(corr.wrapping_add(u8_clamp(diff + i32::from(lower_byte(last.blue)))));
} else {
this_val.blue = last.blue & 0x00FF;
}
diff = i32::from(upper_byte(this_val.red)) - i32::from(upper_byte(last.red));
if color_diff.upper_green_byte_changed() {
corr = decoder.decode_symbol(&mut models.upper_green_byte)? as u8;
this_val.green |= u16::from(
corr.wrapping_add(u8_clamp(diff + i32::from(upper_byte(last.green)))),
) << 8;
} else {
this_val.green |= last.green & 0xFF00;
}
if color_diff.upper_blue_byte_changed() {
corr = decoder.decode_symbol(&mut models.upper_blue_byte)? as u8;
diff = (diff + i32::from(upper_byte(this_val.green))
- i32::from(upper_byte(last.green)))
/ 2;
this_val.blue |=
u16::from(corr.wrapping_add(u8_clamp(diff + i32::from(upper_byte(last.blue)))))
<< 8;
} else {
this_val.blue |= last.blue & 0xFF00;
}
} else {
this_val.green = this_val.red;
this_val.blue = this_val.red;
}
Ok(this_val)
}
impl<R: Read> FieldDecompressor<R> for LasRGBDecompressor {
fn size_of_field(&self) -> usize {
6
}
fn decompress_first(&mut self, src: &mut R, first_point: &mut [u8]) -> std::io::Result<()> {
self.last = read_and_unpack::<_, RGB>(src, first_point)?;
Ok(())
}
fn decompress_with(
&mut self,
decoder: &mut ArithmeticDecoder<R>,
buf: &mut [u8],
) -> std::io::Result<()> {
let this_val = decompress_rgb_using(decoder, &mut self.models, &self.last)?;
self.last = this_val;
this_val.pack_into(buf);
Ok(())
}
}
}
pub mod v3 {
use std::io::{Cursor, Read, Seek, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use crate::decoders::ArithmeticDecoder;
use crate::encoders::ArithmeticEncoder;
use crate::las::rgb::RGB;
use crate::las::utils::{
copy_bytes_into_decoder, copy_encoder_content_to, inner_buffer_len_of, read_and_unpack,
};
use crate::packers::Packable;
use crate::record::{LayeredFieldCompressor, LayeredFieldDecompressor};
use super::v2;
struct LasDecompressionContextRGB {
models: v2::RGBModels,
unused: bool,
}
impl Default for LasDecompressionContextRGB {
fn default() -> Self {
Self {
models: v2::RGBModels::default(),
unused: false,
}
}
}
pub struct LasRGBDecompressor {
decoder: ArithmeticDecoder<Cursor<Vec<u8>>>,
changed_rgb: bool,
requested_rgb: bool,
layer_size: u32,
contexts: [LasDecompressionContextRGB; 4],
last_rgbs: [RGB; 4],
last_context_used: usize,
}
impl Default for LasRGBDecompressor {
fn default() -> Self {
Self {
decoder: ArithmeticDecoder::new(Cursor::new(Vec::<u8>::new())),
changed_rgb: false,
requested_rgb: true,
layer_size: 0,
contexts: [
LasDecompressionContextRGB::default(),
LasDecompressionContextRGB::default(),
LasDecompressionContextRGB::default(),
LasDecompressionContextRGB::default(),
],
last_rgbs: [RGB::default(); 4],
last_context_used: 0,
}
}
}
impl<R: Read + Seek> LayeredFieldDecompressor<R> for LasRGBDecompressor {
fn size_of_field(&self) -> usize {
std::mem::size_of::<u16>() * 3
}
fn init_first_point(
&mut self,
src: &mut R,
first_point: &mut [u8],
context: &mut usize,
) -> std::io::Result<()> {
for rgb_context in &mut self.contexts {
rgb_context.unused = true;
}
self.last_rgbs[*context] = read_and_unpack::<_, RGB>(src, first_point)?;
self.contexts[*context].unused = false;
self.last_context_used = *context;
Ok(())
}
fn decompress_field_with(
&mut self,
current_point: &mut [u8],
context: &mut usize,
) -> std::io::Result<()> {
let mut last_item = &mut self.last_rgbs[self.last_context_used];
if self.last_context_used != *context {
self.last_context_used = *context;
if self.contexts[*context].unused {
self.last_rgbs[*context] = *last_item;
self.contexts[*context].unused = false;
last_item = &mut self.last_rgbs[*context];
}
}
if self.changed_rgb {
let new = v2::decompress_rgb_using(
&mut self.decoder,
&mut self.contexts[self.last_context_used].models,
last_item,
)?;
new.pack_into(current_point);
*last_item = new;
} else {
last_item.pack_into(current_point);
}
Ok(())
}
fn read_layers_sizes(&mut self, src: &mut R) -> std::io::Result<()> {
self.layer_size = src.read_u32::<LittleEndian>()?;
Ok(())
}
fn read_layers(&mut self, src: &mut R) -> std::io::Result<()> {
self.changed_rgb = copy_bytes_into_decoder(
self.requested_rgb,
self.layer_size as usize,
&mut self.decoder,
src,
)?;
Ok(())
}
}
pub struct LasRGBCompressor {
encoder: ArithmeticEncoder<Cursor<Vec<u8>>>,
rgb_has_changed: bool,
contexts: [Option<v2::RGBModels>; 4],
last_rgbs: [Option<RGB>; 4],
last_context_used: usize,
}
impl Default for LasRGBCompressor {
fn default() -> Self {
Self {
encoder: ArithmeticEncoder::new(Cursor::new(Vec::<u8>::new())),
rgb_has_changed: false,
contexts: [None, None, None, None],
last_rgbs: [None; 4],
last_context_used: 0,
}
}
}
impl<R: Write> LayeredFieldCompressor<R> for LasRGBCompressor {
fn size_of_field(&self) -> usize {
RGB::SIZE
}
fn init_first_point(
&mut self,
dst: &mut R,
first_point: &[u8],
context: &mut usize,
) -> std::io::Result<()> {
dst.write_all(first_point)?;
self.contexts[*context] = Some(v2::RGBModels::default());
self.last_rgbs[*context] = Some(RGB::unpack_from(first_point));
self.last_context_used = *context;
Ok(())
}
fn compress_field_with(&mut self, buf: &[u8], context: &mut usize) -> std::io::Result<()> {
let current_point = RGB::unpack_from(buf);
let mut last_rgb = self.last_rgbs[self.last_context_used]
.as_mut()
.expect("internal error: last value is not initialized");
if self.last_context_used != *context {
if self.contexts[*context].is_none() {
self.contexts[*context] = Some(v2::RGBModels::default());
self.last_rgbs[*context] = Some(*last_rgb);
last_rgb = self.last_rgbs[*context].as_mut().unwrap();
}
self.last_context_used = *context;
}
if *last_rgb != current_point {
self.rgb_has_changed = true;
}
let models = self.contexts[self.last_context_used]
.as_mut()
.expect("internal error: context is not initialized");
v2::compress_rgb_using(&mut self.encoder, models, ¤t_point, last_rgb)?;
*last_rgb = current_point;
Ok(())
}
fn write_layers_sizes(&mut self, dst: &mut R) -> std::io::Result<()> {
if self.rgb_has_changed {
self.encoder.done()?;
dst.write_u32::<LittleEndian>(inner_buffer_len_of(&self.encoder) as u32)?;
}
Ok(())
}
fn write_layers(&mut self, dst: &mut R) -> std::io::Result<()> {
if self.rgb_has_changed {
copy_encoder_content_to(&mut self.encoder, dst)?;
}
Ok(())
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn lower_red_changed() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 1,
green: 0,
blue: 0,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00000001);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01000001);
}
#[test]
fn upper_red_changed() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 256,
green: 0,
blue: 0,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00000010);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01000010);
}
#[test]
fn lower_green_changes() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 0,
green: 1,
blue: 0,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00000100);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01000100);
}
#[test]
fn upper_green_changes() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 0,
green: 256,
blue: 0,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00001000);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01001000);
}
#[test]
fn lower_blue_changes() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 0,
green: 0,
blue: 1,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00010000);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01010000);
}
#[test]
fn upper_blue_changes() {
let a = RGB {
red: 0,
green: 0,
blue: 0,
};
let b = RGB {
red: 0,
green: 0,
blue: 256,
};
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00100000);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b01100000);
}
#[test]
fn test_nothing_changes() {
let a = RGB::default();
let b = RGB::default();
assert_eq!(ColorDiff::from_points(&a, &b).0, 0b00000000);
assert_eq!(ColorDiff::from_points(&b, &a).0, 0b00000000);
}
}