use image::{DynamicImage, ImageBuffer, Luma};
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShareType {
Binary,
Grayscale,
Color,
}
#[derive(Debug, Clone)]
pub struct Share {
pub image: DynamicImage,
pub share_type: ShareType,
pub index: usize,
pub total_shares: usize,
pub original_width: u32,
pub original_height: u32,
pub block_size: usize,
pub is_meaningful: bool,
}
impl Share {
pub fn new(
image: DynamicImage,
index: usize,
total_shares: usize,
original_width: u32,
original_height: u32,
block_size: usize,
is_meaningful: bool,
) -> Self {
let share_type = match &image {
DynamicImage::ImageLuma8(_) => ShareType::Binary,
DynamicImage::ImageLuma16(_) => ShareType::Grayscale,
DynamicImage::ImageRgb8(_) | DynamicImage::ImageRgba8(_) => ShareType::Color,
_ => ShareType::Grayscale,
};
Self {
image,
share_type,
index,
total_shares,
original_width,
original_height,
block_size,
is_meaningful,
}
}
pub fn dimensions(&self) -> (u32, u32) {
(self.image.width(), self.image.height())
}
pub fn to_binary(&self) -> ImageBuffer<Luma<u8>, Vec<u8>> {
match &self.image {
DynamicImage::ImageLuma8(img) => {
let mut binary = ImageBuffer::new(img.width(), img.height());
for (x, y, pixel) in img.enumerate_pixels() {
let value = if pixel[0] > 127 { 255 } else { 0 };
binary.put_pixel(x, y, Luma([value]));
}
binary
}
_ => {
let gray = self.image.to_luma8();
let mut binary = ImageBuffer::new(gray.width(), gray.height());
for (x, y, pixel) in gray.enumerate_pixels() {
let value = if pixel[0] > 127 { 255 } else { 0 };
binary.put_pixel(x, y, Luma([value]));
}
binary
}
}
}
pub fn is_compatible(&self, other: &Share) -> bool {
self.dimensions() == other.dimensions()
&& self.total_shares == other.total_shares
&& self.original_width == other.original_width
&& self.original_height == other.original_height
&& self.block_size == other.block_size
}
pub fn stack_binary(&self, other: &Share) -> ImageBuffer<Luma<u8>, Vec<u8>> {
let binary1 = self.to_binary();
let binary2 = other.to_binary();
let (width, height) = self.dimensions();
let mut result = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let p1 = binary1.get_pixel(x, y)[0];
let p2 = binary2.get_pixel(x, y)[0];
let value = if p1 == 0 && p2 == 0 { 0 } else { 255 };
result.put_pixel(x, y, Luma([value]));
}
}
result
}
pub fn stack_and(&self, other: &Share) -> ImageBuffer<Luma<u8>, Vec<u8>> {
let binary1 = self.to_binary();
let binary2 = other.to_binary();
let (width, height) = self.dimensions();
let mut result = ImageBuffer::new(width, height);
for y in 0..height {
for x in 0..width {
let p1 = binary1.get_pixel(x, y)[0];
let p2 = binary2.get_pixel(x, y)[0];
let value = if p1 == 0 || p2 == 0 { 0 } else { 255 };
result.put_pixel(x, y, Luma([value]));
}
}
result
}
pub fn save(&self, path: &str) -> Result<(), image::ImageError> {
self.image.save(path)
}
}
impl fmt::Display for Share {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Share {}/{}: {:?} {}x{} (original: {}x{}, block_size: {})",
self.index,
self.total_shares,
self.share_type,
self.image.width(),
self.image.height(),
self.original_width,
self.original_height,
self.block_size
)
}
}
pub fn stack_shares(shares: &[Share]) -> Option<ImageBuffer<Luma<u8>, Vec<u8>>> {
if shares.is_empty() {
return None;
}
let first = &shares[0];
for share in shares.iter().skip(1) {
if !first.is_compatible(share) {
return None;
}
}
let mut result = shares[0].to_binary();
for share in shares.iter().skip(1) {
let binary = share.to_binary();
for y in 0..result.height() {
for x in 0..result.width() {
let current = result.get_pixel(x, y)[0];
let new = binary.get_pixel(x, y)[0];
let value = if current == 0 && new == 0 { 0 } else { 255 };
result.put_pixel(x, y, Luma([value]));
}
}
}
Some(result)
}
pub fn progressive_stack(shares: &[Share]) -> Vec<ImageBuffer<Luma<u8>, Vec<u8>>> {
let mut results = Vec::new();
for i in 2..=shares.len() {
if let Some(stacked) = stack_shares(&shares[0..i]) {
results.push(stacked);
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_share_creation() {
let img = DynamicImage::new_luma8(100, 100);
let share = Share::new(img, 1, 3, 50, 50, 2, false);
assert_eq!(share.index, 1);
assert_eq!(share.total_shares, 3);
assert_eq!(share.share_type, ShareType::Binary);
assert_eq!(share.dimensions(), (100, 100));
}
#[test]
fn test_share_compatibility() {
let img1 = DynamicImage::new_luma8(100, 100);
let img2 = DynamicImage::new_luma8(100, 100);
let img3 = DynamicImage::new_luma8(200, 200);
let share1 = Share::new(img1, 1, 3, 50, 50, 2, false);
let share2 = Share::new(img2, 2, 3, 50, 50, 2, false);
let share3 = Share::new(img3, 3, 3, 50, 50, 2, false);
assert!(share1.is_compatible(&share2));
assert!(!share1.is_compatible(&share3));
}
}