109 lines
3.3 KiB
Rust
109 lines
3.3 KiB
Rust
use eframe::egui::{Color32, ColorImage};
|
|
use qrcode::{Color, QrCode};
|
|
|
|
pub type QrError = qrcode::types::QrError;
|
|
pub const QR_QUIET_ZONE: usize = 4;
|
|
|
|
/// Generates a 2D boolean matrix of QR modules (true = dark, false = light) including a quiet zone of 4 modules.
|
|
pub fn generate_qr_matrix(data: &str) -> Result<Vec<Vec<bool>>, QrError> {
|
|
let code = QrCode::new(data.as_bytes())?;
|
|
let colors = code.to_colors();
|
|
let width = code.width();
|
|
let quiet_zone = QR_QUIET_ZONE;
|
|
let total_size = width + quiet_zone * 2;
|
|
|
|
let mut matrix = vec![vec![false; total_size]; total_size];
|
|
|
|
for y in 0..width {
|
|
for x in 0..width {
|
|
let is_dark = colors[y * width + x] == Color::Dark;
|
|
matrix[y + quiet_zone][x + quiet_zone] = is_dark;
|
|
}
|
|
}
|
|
|
|
Ok(matrix)
|
|
}
|
|
|
|
/// Renders a terminal-friendly QR code using Unicode full blocks and ANSI colors with a 4-module quiet zone.
|
|
pub fn render_terminal_qr(data: &str) -> Result<String, QrError> {
|
|
let code = QrCode::new(data.as_bytes())?;
|
|
let colors = code.to_colors();
|
|
let width = code.width();
|
|
let quiet_zone = QR_QUIET_ZONE;
|
|
let total_size = width + quiet_zone * 2;
|
|
|
|
let mut out = String::new();
|
|
|
|
for y in 0..total_size {
|
|
for x in 0..total_size {
|
|
let is_dark = if x >= quiet_zone
|
|
&& x < quiet_zone + width
|
|
&& y >= quiet_zone
|
|
&& y < quiet_zone + width
|
|
{
|
|
let qx = x - quiet_zone;
|
|
let qy = y - quiet_zone;
|
|
colors[qy * width + qx] == Color::Dark
|
|
} else {
|
|
false
|
|
};
|
|
|
|
if is_dark {
|
|
out.push_str("██");
|
|
} else {
|
|
out.push_str(" ");
|
|
}
|
|
}
|
|
out.push('\n');
|
|
}
|
|
|
|
Ok(out)
|
|
}
|
|
|
|
/// Renders a high-contrast ColorImage for egui rendering with a 4-module quiet zone and configurable scale.
|
|
pub fn render_egui_image(data: &str, scale: usize) -> Result<ColorImage, QrError> {
|
|
let code = QrCode::new(data.as_bytes())?;
|
|
let colors = code.to_colors();
|
|
let width = code.width();
|
|
let quiet_zone = QR_QUIET_ZONE;
|
|
let total_modules = width + quiet_zone * 2;
|
|
|
|
let scale = scale.max(1);
|
|
let image_width = total_modules * scale;
|
|
let image_height = total_modules * scale;
|
|
|
|
let mut pixels = Vec::with_capacity(image_width * image_height);
|
|
|
|
for my in 0..total_modules {
|
|
for _sy in 0..scale {
|
|
for mx in 0..total_modules {
|
|
let is_dark = if mx >= quiet_zone
|
|
&& mx < quiet_zone + width
|
|
&& my >= quiet_zone
|
|
&& my < quiet_zone + width
|
|
{
|
|
let qx = mx - quiet_zone;
|
|
let qy = my - quiet_zone;
|
|
colors[qy * width + qx] == Color::Dark
|
|
} else {
|
|
false
|
|
};
|
|
|
|
let color = if is_dark {
|
|
Color32::from_rgb(18, 18, 20)
|
|
} else {
|
|
Color32::from_rgb(255, 255, 255)
|
|
};
|
|
|
|
for _sx in 0..scale {
|
|
pixels.push(color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(ColorImage {
|
|
size: [image_width, image_height],
|
|
pixels,
|
|
})
|
|
}
|