use crate::layout::{SampleBits, SampleParts};
use image_texel::{AsTexel, Texel};
#[derive(Clone, Copy, Debug)]
pub struct FromBits {
pub(crate) begin: usize,
pub(crate) len: usize,
}
macro_rules! from_bits {
($bits:ident = { $($variant:pat => $($value:expr)+);* }) => {
match $bits {
$($variant => from_bits!(@ $($value);*)),*,
}
};
(@ $v0:expr) => {
[Some(FromBits::from_range($v0)), None, None, None, None, None, None, None]
};
(@ $v0:expr; $v1:expr) => {
[Some(FromBits::from_range($v0)), Some(FromBits::from_range($v1)), None, None, None, None, None, None]
};
(@ $v0:expr; $v1:expr; $v2:expr) => {
[
Some(FromBits::from_range($v0)),
Some(FromBits::from_range($v1)),
Some(FromBits::from_range($v2)),
None,
None,
None,
None,
None,
]
};
(@ $v0:expr; $v1:expr; $v2:expr; $v3:expr) => {
[
Some(FromBits::from_range($v0)),
Some(FromBits::from_range($v1)),
Some(FromBits::from_range($v2)),
Some(FromBits::from_range($v3)),
None,
None,
None,
None,
]
};
(@ $v0:expr; $v1:expr; $v2:expr; $v3:expr; $v4:expr; $v5:expr) => {
[
Some(FromBits::from_range($v0)),
Some(FromBits::from_range($v1)),
Some(FromBits::from_range($v2)),
Some(FromBits::from_range($v3)),
Some(FromBits::from_range($v4)),
Some(FromBits::from_range($v5)),
None,
None,
]
};
(@ $v0:expr; $v1:expr; $v2:expr; $v3:expr; $v4:expr; $v5:expr; $v6:expr; $v7:expr) => {
[
Some(FromBits::from_range($v0)),
Some(FromBits::from_range($v1)),
Some(FromBits::from_range($v2)),
Some(FromBits::from_range($v3)),
Some(FromBits::from_range($v4)),
Some(FromBits::from_range($v5)),
Some(FromBits::from_range($v6)),
Some(FromBits::from_range($v7)),
]
};
}
impl FromBits {
const NO_BITS: Self = FromBits { begin: 0, len: 0 };
const fn from_range(range: core::ops::Range<usize>) -> Self {
FromBits {
begin: range.start,
len: range.end - range.start,
}
}
pub(crate) fn for_pixel(bits: SampleBits, parts: SampleParts) -> [Self; 4] {
let mut vals = [Self::NO_BITS; 4];
let bits = Self::bits(bits);
let channels = parts.channels();
for (bits, (channel, pos)) in bits.zip(channels) {
if let Some(_) = channel {
vals[pos as usize] = bits;
}
}
vals
}
pub(crate) fn for_pixels<const N: usize>(
bits: SampleBits,
parts: SampleParts,
) -> [[Self; 4]; N] {
let mut vals = [[Self::NO_BITS; 4]; N];
let mut bits = Self::bits(bits);
for vals in vals.iter_mut() {
let channels = parts.channels().filter_map(|(ch, p)| Some((ch?, p)));
for (_, pos) in channels {
if let Some(bits) = bits.next() {
vals[pos as usize] = bits;
}
}
}
vals
}
pub(crate) const fn mask(self) -> u32 {
((-1i64 as u64) ^ u32::MAX as u64).rotate_left(self.len as u32) as u32
}
fn bits(bits: SampleBits) -> impl Iterator<Item = Self> {
use SampleBits::*;
let filled: [Option<Self>; 8] = from_bits!(bits = {
Int8 | UInt8 => 0..8;
UInt332 => 0..3 3..6 6..8;
UInt233 => 0..2 2..5 5..8;
Int16 | UInt16 => 0..16;
UInt4x2 => 0..4 4..8;
UInt4x4 => 0..4 4..8 8..12 12..16;
UInt4x6 => 0..4 4..8 8..12 12..16 16..20 20..24;
UInt_444 => 4..8 8..12 12..16;
UInt444_ => 0..4 4..8 8..12;
UInt565 => 0..5 5..11 11..16;
Int8x2 | UInt8x2 => 0..8 8..16;
Int8x3 | UInt8x3 => 0..8 8..16 16..24;
Int8x4 | UInt8x4 => 0..8 8..16 16..24 24..32;
UInt8x6 => 0..8 8..16 16..24 24..32 32..40 40..48;
Int16x2 | UInt16x2 => 0..16 16..32;
Int16x3 | UInt16x3 => 0..16 16..32 32..48;
Int16x4 | UInt16x4 => 0..16 16..32 32..48 48..64;
UInt16x6 => 0..16 16..32 32..48 48..64 64..80 80..96;
UInt1010102 => 0..10 10..20 20..30 30..32;
UInt2101010 => 0..2 2..12 12..22 22..32;
UInt101010_ => 0..10 10..20 20..30;
UInt_101010 => 2..12 12..22 22..32;
Float16x4 => 0..16 16..32 32..48 48..64;
Float32 => 0..32;
Float32x2 => 0..32 32..64;
Float32x3 => 0..32 32..64 64..96;
Float32x4 => 0..32 32..64 64..96 96..128;
Float32x6 => 0..32 32..64 64..96 96..128 128..160 160..192;
UInt1x8 => 0..1 1..2 2..3 3..4 4..5 5..6 6..7 7..8;
UInt2x4 => 0..2 2..4 4..6 6..8
});
filled.into_iter().filter_map(|x| x)
}
#[inline]
pub(crate) fn extract_as_lsb<T>(&self, texel: Texel<T>, val: &T) -> u32 {
let ne_bytes = texel.to_bytes(core::slice::from_ref(val));
let start_byte = self.begin / 8;
let from_bytes = &ne_bytes[start_byte.min(ne_bytes.len())..];
assert!(self.len <= 32);
let shift = self.begin - start_byte * 8;
let bitlen = self.len + shift;
let copylen = if bitlen % 8 == 0 {
bitlen / 8
} else {
bitlen / 8 + 1
};
let mut be_bytes = [0; 8];
let initlen = copylen.min(8).min(from_bytes.len());
be_bytes[..initlen].copy_from_slice(&from_bytes[..initlen]);
let val = u64::from_be_bytes(be_bytes) >> (64 - bitlen).min(63);
val as u32 & self.mask()
}
pub(crate) fn insert_as_lsb<T>(&self, texel: Texel<T>, val: &mut T, bits: u32) {
let ne_bytes = texel.to_mut_bytes(core::slice::from_mut(val));
let start_byte = self.begin / 8;
let bytestart = start_byte.min(ne_bytes.len());
let texel_bytes = &mut ne_bytes[bytestart..];
let shift = self.begin - start_byte * 8;
let bitlen = self.len + shift;
let copylen = if bitlen % 8 == 0 {
bitlen / 8
} else {
bitlen / 8 + 1
};
let mut be_bytes = [0; 8];
let initlen = copylen.min(8).min(texel_bytes.len());
be_bytes[..initlen].copy_from_slice(&texel_bytes[..initlen]);
let mask = ((-1i64 as u64) ^ u32::MAX as u64).rotate_left((self.len as u32).min(32))
& (u32::MAX as u64);
let bitshift = (64 - bitlen).min(63);
let newval = (u64::from_be_bytes(be_bytes) & !(mask << bitshift))
| (u64::from(bits) & mask) << bitshift;
be_bytes = newval.to_be_bytes();
texel_bytes[..initlen].copy_from_slice(&be_bytes[..initlen]);
}
}
#[cfg(test)]
mod tests {
use super::FromBits;
use image_texel::AsTexel;
#[test]
fn bit_extraction() {
fn extract_simple(r: core::ops::Range<usize>, val: &u8) -> u32 {
FromBits {
begin: r.start,
len: r.len(),
}
.extract_as_lsb(u8::texel(), &val)
}
let val = 0b1000_1010u8;
assert_eq!(extract_simple(0..1, &val), 1);
assert_eq!(extract_simple(1..2, &val), 0);
assert_eq!(extract_simple(2..3, &val), 0);
assert_eq!(extract_simple(6..7, &val), 1);
assert_eq!(extract_simple(0..7, &val), val as u32 >> 1);
assert_eq!(extract_simple(1..8, &val), (val & 0x7f) as u32);
assert_eq!(extract_simple(0..0, &val), 0);
assert_eq!(extract_simple(1..1, &val), 0);
}
#[test]
fn bit_insertion() {
fn insert_simple(r: core::ops::Range<usize>, bits: u32, val: &mut u8) -> u8 {
let before = *val;
FromBits {
begin: r.start,
len: r.len(),
}
.insert_as_lsb(u8::texel(), val, bits);
before ^ *val
}
let mut val = 0b1000_1010u8;
assert_eq!(insert_simple(0..1, 1, &mut val), 0);
assert_eq!(insert_simple(1..2, 0, &mut val), 0);
assert_eq!(insert_simple(2..3, 0, &mut val), 0);
assert_eq!(insert_simple(6..7, 1, &mut val), 0);
assert_eq!(insert_simple(0..7, val as u32 >> 1, &mut val), 0);
assert_eq!(insert_simple(1..8, (val & 0x7f) as u32, &mut val), 0);
assert_eq!(insert_simple(0..0, 0, &mut val), 0);
assert_eq!(insert_simple(1..1, 0, &mut val), 0);
}
}