use std::borrow::Cow;
use std::ops;
use image::{DynamicImage, GenericImageView, GrayImage, ImageBuffer, Pixel, imageops};
use crate::BitOrder;
pub trait HashBytes {
fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self
where
Self: Sized;
fn max_bits() -> usize;
fn as_slice(&self) -> &[u8];
}
impl HashBytes for Box<[u8]> {
fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self {
iter.collect::<Vec<u8>>().into_boxed_slice()
}
fn max_bits() -> usize {
usize::MAX
}
fn as_slice(&self) -> &[u8] {
self
}
}
impl HashBytes for Vec<u8> {
fn from_iter<I: Iterator<Item = u8>>(iter: I) -> Self {
iter.collect()
}
fn max_bits() -> usize {
usize::MAX
}
fn as_slice(&self) -> &[u8] {
self
}
}
macro_rules! hash_bytes_array {
($($n:expr),*) => {$(
impl HashBytes for [u8; $n] {
fn from_iter<I: Iterator<Item=u8>>(mut iter: I) -> Self {
let mut out = [0; $n];
for (src, dest) in iter.by_ref().zip(out.as_mut()) {
*dest = src;
}
out
}
fn max_bits() -> usize {
$n * 8
}
fn as_slice(&self) -> &[u8] { self }
}
)*}
}
hash_bytes_array!(8, 16, 24, 32, 40, 48, 56, 64);
struct BoolsToBytes<I> {
iter: I,
bit_order: BitOrder,
}
impl<I> Iterator for BoolsToBytes<I>
where
I: Iterator<Item = bool>,
{
type Item = u8;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
match self.bit_order {
BitOrder::LsbFirst => {
self.iter
.by_ref()
.take(8)
.enumerate()
.fold(None, |accum, (n, val)| {
accum.or(Some(0)).map(|accum| accum | ((val as u8) << n))
})
}
BitOrder::MsbFirst => {
self.iter
.by_ref()
.take(8)
.enumerate()
.fold(None, |accum, (n, val)| {
accum
.or(Some(0))
.map(|accum| accum | ((val as u8) << (7 - n)))
})
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (lower, upper) = self.iter.size_hint();
(
lower / 8,
upper.map(|upper| {
if upper % 8 == 0 {
upper / 8
} else {
upper / 8 + 1
}
}),
)
}
}
pub(crate) trait BitSet: HashBytes {
fn from_bools<I: Iterator<Item = bool>>(iter: I, bit_order: BitOrder) -> Self
where
Self: Sized,
{
Self::from_iter(BoolsToBytes { iter, bit_order })
}
fn hamming(&self, other: &Self) -> u32 {
self.as_slice()
.iter()
.zip(other.as_slice())
.map(|(l, r)| (l ^ r).count_ones())
.sum()
}
}
impl<T: HashBytes> BitSet for T {}
pub trait Image: GenericImageView + 'static {
type Buf: Image + DiffImage;
fn to_grayscale(&self) -> Cow<'_, GrayImage>;
fn blur(&self, sigma: f32) -> Self::Buf;
fn foreach_pixel8<F>(&self, foreach: F)
where
F: FnMut(u32, u32, &[u8]);
}
pub trait DiffImage {
fn diff_inplace(&mut self, other: &Self);
}
#[cfg(not(feature = "nightly"))]
impl<P, C> Image for ImageBuffer<P, C>
where
P: Pixel<Subpixel = u8> + 'static,
C: ops::Deref<Target = [u8]> + 'static,
{
type Buf = ImageBuffer<P, Vec<u8>>;
fn to_grayscale(&self) -> Cow<'_, GrayImage> {
Cow::Owned(imageops::grayscale(self))
}
fn blur(&self, sigma: f32) -> Self::Buf {
imageops::blur(self, sigma)
}
fn foreach_pixel8<F>(&self, mut foreach: F)
where
F: FnMut(u32, u32, &[u8]),
{
self.enumerate_pixels()
.for_each(|(x, y, px)| foreach(x, y, px.channels()));
}
}
#[cfg(feature = "nightly")]
impl<P, C> Image for ImageBuffer<P, C>
where
P: Pixel<Subpixel = u8> + 'static,
C: ops::Deref<Target = [u8]> + 'static,
{
type Buf = ImageBuffer<P, Vec<u8>>;
default fn to_grayscale(&self) -> Cow<'_, GrayImage> {
Cow::Owned(imageops::grayscale(self))
}
default fn blur(&self, sigma: f32) -> Self::Buf {
imageops::blur(self, sigma)
}
default fn foreach_pixel8<F>(&self, mut foreach: F)
where
F: FnMut(u32, u32, &[u8]),
{
self.enumerate_pixels()
.for_each(|(x, y, px)| foreach(x, y, px.channels()));
}
}
impl<P> DiffImage for ImageBuffer<P, Vec<u8>>
where
P: Pixel<Subpixel = u8> + 'static,
{
fn diff_inplace(&mut self, other: &Self) {
self.iter_mut().zip(other.iter()).for_each(|(l, r)| {
*l = 255 - r.saturating_sub(*l);
});
}
}
impl Image for DynamicImage {
type Buf = image::RgbaImage;
fn to_grayscale(&self) -> Cow<'_, GrayImage> {
self.as_luma8()
.map_or_else(|| Cow::Owned(self.to_luma8()), Cow::Borrowed)
}
fn blur(&self, sigma: f32) -> Self::Buf {
imageops::blur(self, sigma)
}
fn foreach_pixel8<F>(&self, mut foreach: F)
where
F: FnMut(u32, u32, &[u8]),
{
self.pixels()
.for_each(|(x, y, px)| foreach(x, y, px.channels()));
}
}
#[cfg(feature = "nightly")]
impl Image for GrayImage {
fn to_grayscale(&self) -> Cow<'_, GrayImage> {
Cow::Borrowed(self)
}
}
#[test]
fn test_bools_to_bytes() {
let bools = (0..16).map(|x| x & 1 == 0);
let bytes = Vec::from_bools(bools.clone(), BitOrder::LsbFirst);
assert_eq!(*bytes, [0b01010101; 2]);
let bools_to_bytes = BoolsToBytes {
iter: bools,
bit_order: BitOrder::LsbFirst,
};
assert_eq!(bools_to_bytes.size_hint(), (2, Some(2)));
}
#[test]
fn test_bit_order() {
let bools = (0..16).map(|x| x % 3 == 0);
let bytes_lsb = Vec::from_bools(bools.clone(), BitOrder::LsbFirst);
assert_eq!(*bytes_lsb, [0b01001001, 0b10010010]);
let bytes_msb = Vec::from_bools(bools, BitOrder::MsbFirst);
assert_eq!(*bytes_msb, [0b10010010, 0b01001001]);
}