#![cfg(feature = "graphics")]
use crate::errors::{PrinterError, Result};
use image::{DynamicImage, GenericImage, GenericImageView, Rgba};
use std::fmt;
#[derive(Debug, Default, Clone, Copy)]
pub enum BitImageSize {
#[default]
Normal,
DoubleWidth,
DoubleHeight,
DoubleWidthAndHeight,
}
impl fmt::Display for BitImageSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BitImageSize::Normal => write!(f, "Normal"),
BitImageSize::DoubleWidth => write!(f, "Double width"),
BitImageSize::DoubleHeight => write!(f, "Double height"),
BitImageSize::DoubleWidthAndHeight => write!(f, "Double width and height"),
}
}
}
impl From<&BitImageSize> for u8 {
fn from(size: &BitImageSize) -> Self {
match size {
BitImageSize::Normal => 0,
BitImageSize::DoubleWidth => 1,
BitImageSize::DoubleHeight => 2,
BitImageSize::DoubleWidthAndHeight => 3,
}
}
}
#[derive(Debug)]
pub struct BitImageOption {
max_width: Option<u32>,
max_height: Option<u32>,
size: BitImageSize,
}
impl Default for BitImageOption {
fn default() -> Self {
Self {
max_width: Some(512),
max_height: Some(512),
size: BitImageSize::Normal,
}
}
}
impl BitImageOption {
pub fn new(max_width: Option<u32>, max_height: Option<u32>, size: BitImageSize) -> Result<Self> {
if let Some(max_width) = max_width {
if max_width % 8 != 0 {
return Err(PrinterError::Input(
"bit image max width must be a multiple of 8".to_owned(),
));
}
}
if let Some(max_height) = max_height {
if max_height % 8 != 0 {
return Err(PrinterError::Input(
"bit image max height must be a multiple of 8".to_owned(),
));
}
}
Ok(Self {
max_width,
max_height,
size,
})
}
}
#[derive(Debug)]
pub struct BitImage {
path: String,
image: DynamicImage,
option: BitImageOption,
}
impl BitImage {
pub fn new(path: &str, option: BitImageOption) -> Result<Self> {
let img = image::open(path)?;
Self::from_dynamic_image(img, option, path)
}
pub fn from_bytes(bytes: &[u8], option: BitImageOption) -> Result<Self> {
let img = image::load_from_memory(bytes)?;
Self::from_dynamic_image(img, option, "")
}
fn from_dynamic_image(img: DynamicImage, option: BitImageOption, path: &str) -> Result<Self> {
let mut img = match (option.max_width, option.max_height) {
(Some(max_width), None) => {
if img.width() > max_width {
img.resize(max_width, max_width, image::imageops::Nearest)
} else {
img
}
}
(None, Some(max_height)) => {
if img.height() > max_height {
img.resize(max_height, max_height, image::imageops::Nearest)
} else {
img
}
}
(Some(max_width), Some(max_height)) => {
if img.width() > max_width || img.height() > max_height {
img.resize(max_width, max_height, image::imageops::Nearest)
} else {
img
}
}
_ => img,
};
Self::remove_alpha(&mut img);
img = img.grayscale();
Ok(Self {
path: path.to_string(),
image: img,
option,
})
}
fn remove_alpha(img: &mut DynamicImage) {
for y in 0..img.height() {
for x in 0..img.width() {
let old = img.get_pixel(x, y);
let alpha = old.0[3] as u32;
let inverse_alpha = 255 - alpha;
let new = Rgba::from([
((old.0[0] as u32 * alpha + inverse_alpha * 255) / 255) as u8,
((old.0[1] as u32 * alpha + inverse_alpha * 255) / 255) as u8,
((old.0[2] as u32 * alpha + inverse_alpha * 255) / 255) as u8,
255,
]);
img.put_pixel(x, y, new);
}
}
}
fn width(&self) -> Result<u16> {
Ok(u16::try_from(self.image.width())?)
}
fn height(&self) -> Result<u16> {
Ok(u16::try_from(self.image.height())?)
}
pub fn width_bytes(&self) -> Result<u16> {
Ok(u16::try_from((f32::from(self.width()?) / 8.0).ceil() as usize)?)
}
pub fn image(&self) -> &DynamicImage {
&self.image
}
pub fn pixel(&self, x: u32, y: u32) -> Rgba<u8> {
self.image.get_pixel(x, y)
}
pub fn path(&self) -> &str {
&self.path
}
pub fn size(&self) -> &BitImageSize {
&self.option.size
}
pub fn with_bytes_u8(&self) -> Result<Vec<u8>> {
let width = self.width_bytes()?;
let xh = width / 256;
let xl = width
.checked_add_signed(-256 * i16::try_from(xh)?)
.ok_or(PrinterError::Input("Bit image width invalid data".to_owned()))?;
Ok(vec![u8::try_from(xl)?, u8::try_from(xh)?])
}
pub fn height_u8(&self) -> Result<Vec<u8>> {
let height = self.height()?;
let yh = height / 256;
let yl = height
.checked_add_signed(-256 * i16::try_from(yh)?)
.ok_or(PrinterError::Input("Bit image height invalid data".to_owned()))?;
Ok(vec![u8::try_from(yl)?, u8::try_from(yh)?])
}
fn is_pixel_black(&self, x: u16, y: u16) -> bool {
self.pixel(u32::from(x), u32::from(y)).0[0] <= 128
}
pub fn raster_data(&self) -> Result<Vec<u8>> {
let width = self.width()?;
let height = self.height()?;
let mut data = Vec::new();
for y in 0..height {
for x in (0..width).step_by(8) {
let mut byte = 0;
for bit in 0..8 {
let x_offset = x + bit;
if x_offset >= width {
byte <<= 8 - bit;
break;
}
byte = (byte << 1) | u8::from(self.is_pixel_black(x_offset, y));
}
data.push(byte);
}
}
Ok(data)
}
}