#![forbid(unsafe_code)]
use image::{GrayImage, Luma};
use super::types::{BarcodeOutput, LinearBarcode, MatrixBarcode};
const WHITE: Luma<u8> = Luma([255u8]);
const BLACK: Luma<u8> = Luma([0u8]);
impl BarcodeOutput {
pub fn to_image(&self, module_size: u32) -> GrayImage {
assert!(module_size > 0, "module_size must be positive");
match self {
BarcodeOutput::Linear(lb) => render_linear_image(lb, module_size),
BarcodeOutput::Matrix(mb) => render_matrix_image(mb, module_size),
}
}
}
fn render_linear_image(lb: &LinearBarcode, module_size: u32) -> GrayImage {
let quiet = 10 * module_size;
let width = lb.bars.len() as u32 * module_size + 2 * quiet;
let height = lb.height * module_size + 2 * quiet;
let mut img = GrayImage::from_pixel(width, height, WHITE);
for (i, &dark) in lb.bars.iter().enumerate() {
if dark {
let x_start = quiet + i as u32 * module_size;
for dx in 0..module_size {
for dy in 0..(lb.height * module_size) {
img.put_pixel(x_start + dx, quiet + dy, BLACK);
}
}
}
}
img
}
fn render_matrix_image(mb: &MatrixBarcode, module_size: u32) -> GrayImage {
let quiet = 4 * module_size;
let width = mb.width as u32 * module_size + 2 * quiet;
let height = mb.height as u32 * module_size + 2 * quiet;
let mut img = GrayImage::from_pixel(width, height, WHITE);
for (row_idx, row) in mb.modules.iter().enumerate() {
for (col_idx, &dark) in row.iter().enumerate() {
if dark {
let x_start = quiet + col_idx as u32 * module_size;
let y_start = quiet + row_idx as u32 * module_size;
for dx in 0..module_size {
for dy in 0..module_size {
img.put_pixel(x_start + dx, y_start + dy, BLACK);
}
}
}
}
}
img
}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::vec;
use image::Luma;
use super::*;
use crate::common::types::{LinearBarcode, MatrixBarcode};
#[test]
fn linear_to_image_dimensions() {
let lb = LinearBarcode {
bars: vec![true, false, true],
height: 10,
text: None,
};
let output = BarcodeOutput::Linear(lb);
let module_size: u32 = 2;
let img = output.to_image(module_size);
let expected_width = 3 * module_size + 2 * 10 * module_size; let expected_height = 10 * module_size + 2 * 10 * module_size; assert_eq!(img.width(), expected_width);
assert_eq!(img.height(), expected_height);
}
#[test]
fn linear_to_image_quiet_zone_is_white() {
let lb = LinearBarcode {
bars: vec![true, false, true],
height: 10,
text: None,
};
let output = BarcodeOutput::Linear(lb);
let img = output.to_image(1);
assert_eq!(*img.get_pixel(0, 0), Luma([255u8]));
assert_eq!(
*img.get_pixel(img.width() - 1, img.height() - 1),
Luma([255u8])
);
}
#[test]
fn linear_to_image_first_dark_bar_is_black() {
let lb = LinearBarcode {
bars: vec![true, false, true],
height: 10,
text: None,
};
let output = BarcodeOutput::Linear(lb);
let module_size: u32 = 1;
let img = output.to_image(module_size);
let quiet = 10 * module_size;
assert_eq!(*img.get_pixel(quiet, quiet), Luma([0u8]));
}
#[test]
fn linear_to_image_light_bar_stays_white() {
let lb = LinearBarcode {
bars: vec![false, true],
height: 5,
text: None,
};
let output = BarcodeOutput::Linear(lb);
let module_size: u32 = 2;
let img = output.to_image(module_size);
let quiet = 10 * module_size;
assert_eq!(*img.get_pixel(quiet, quiet), Luma([255u8]));
assert_eq!(*img.get_pixel(quiet + module_size, quiet), Luma([0u8]));
}
#[test]
fn matrix_to_image_dimensions() {
let mb = MatrixBarcode {
modules: vec![vec![true, false], vec![false, true]],
width: 2,
height: 2,
};
let output = BarcodeOutput::Matrix(mb);
let module_size: u32 = 3;
let img = output.to_image(module_size);
let expected_width = 2 * module_size + 2 * 4 * module_size; let expected_height = 2 * module_size + 2 * 4 * module_size; assert_eq!(img.width(), expected_width);
assert_eq!(img.height(), expected_height);
}
#[test]
fn matrix_to_image_quiet_zone_is_white() {
let mb = MatrixBarcode {
modules: vec![vec![true, false], vec![false, true]],
width: 2,
height: 2,
};
let output = BarcodeOutput::Matrix(mb);
let img = output.to_image(1);
assert_eq!(*img.get_pixel(0, 0), Luma([255u8]));
assert_eq!(
*img.get_pixel(img.width() - 1, img.height() - 1),
Luma([255u8])
);
}
#[test]
fn matrix_to_image_first_dark_module_is_black() {
let mb = MatrixBarcode {
modules: vec![vec![true, false], vec![false, true]],
width: 2,
height: 2,
};
let output = BarcodeOutput::Matrix(mb);
let module_size: u32 = 1;
let img = output.to_image(module_size);
let quiet = 4 * module_size;
assert_eq!(*img.get_pixel(quiet, quiet), Luma([0u8]));
}
#[test]
fn matrix_to_image_light_module_stays_white() {
let mb = MatrixBarcode {
modules: vec![vec![true, false], vec![false, true]],
width: 2,
height: 2,
};
let output = BarcodeOutput::Matrix(mb);
let module_size: u32 = 2;
let img = output.to_image(module_size);
let quiet = 4 * module_size;
assert_eq!(*img.get_pixel(quiet + module_size, quiet), Luma([255u8]));
}
#[test]
fn matrix_to_image_module_fills_entire_block() {
let mb = MatrixBarcode {
modules: vec![vec![false, false], vec![false, true]],
width: 2,
height: 2,
};
let output = BarcodeOutput::Matrix(mb);
let module_size: u32 = 3;
let img = output.to_image(module_size);
let quiet = 4 * module_size;
let x_start = quiet + module_size;
let y_start = quiet + module_size;
for dx in 0..module_size {
for dy in 0..module_size {
assert_eq!(
*img.get_pixel(x_start + dx, y_start + dy),
Luma([0u8]),
"pixel ({}, {}) in dark block should be black",
x_start + dx,
y_start + dy,
);
}
}
}
#[test]
fn ean13_to_image_produces_valid_image() {
use crate::common::traits::BarcodeEncoder;
use crate::ean_upc::ean13::Ean13;
let output = Ean13::encode("5901234123457").unwrap();
let img = output.to_image(2);
assert!(img.width() > 0);
assert!(img.height() > 0);
assert_eq!(*img.get_pixel(0, 0), Luma([255u8]));
}
#[test]
fn datamatrix_to_image_produces_valid_image() {
use crate::common::traits::BarcodeEncoder;
use crate::twod::datamatrix::DataMatrix;
let output = DataMatrix::encode("HELLO").unwrap();
let img = output.to_image(2);
assert!(img.width() > 0);
assert!(img.height() > 0);
assert_eq!(*img.get_pixel(0, 0), Luma([255u8]));
}
}