forked from AbleOS/ableos
192 lines
6.7 KiB
Rust
192 lines
6.7 KiB
Rust
//! Render a QR code into image.
|
||
|
||
use crate::cast::As;
|
||
use crate::types::Color;
|
||
use core::cmp::max;
|
||
|
||
pub mod image;
|
||
pub mod string;
|
||
pub mod svg;
|
||
pub mod unicode;
|
||
|
||
//------------------------------------------------------------------------------
|
||
//{{{ Pixel trait
|
||
|
||
/// Abstraction of an image pixel.
|
||
pub trait Pixel: Copy + Sized {
|
||
/// Type of the finalized image.
|
||
type Image: Sized + 'static;
|
||
|
||
/// The type that stores an intermediate buffer before finalizing to a
|
||
/// concrete image
|
||
type Canvas: Canvas<Pixel = Self, Image = Self::Image>;
|
||
|
||
/// Obtains the default module size. The result must be at least 1×1.
|
||
fn default_unit_size() -> (u32, u32) {
|
||
(8, 8)
|
||
}
|
||
|
||
/// Obtains the default pixel color when a module is dark or light.
|
||
fn default_color(color: Color) -> Self;
|
||
}
|
||
|
||
/// Rendering canvas of a QR code image.
|
||
pub trait Canvas: Sized {
|
||
type Pixel: Sized;
|
||
type Image: Sized;
|
||
|
||
/// Constructs a new canvas of the given dimensions.
|
||
fn new(width: u32, height: u32, dark_pixel: Self::Pixel, light_pixel: Self::Pixel) -> Self;
|
||
|
||
/// Draws a single dark pixel at the (x, y) coordinate.
|
||
fn draw_dark_pixel(&mut self, x: u32, y: u32);
|
||
|
||
fn draw_dark_rect(&mut self, left: u32, top: u32, width: u32, height: u32) {
|
||
for y in top..(top + height) {
|
||
for x in left..(left + width) {
|
||
self.draw_dark_pixel(x, y);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Finalize the canvas to a real image.
|
||
fn into_image(self) -> Self::Image;
|
||
}
|
||
|
||
//}}}
|
||
//------------------------------------------------------------------------------
|
||
//{{{ Renderer
|
||
|
||
/// A QR code renderer. This is a builder type which converts a bool-vector into
|
||
/// an image.
|
||
pub struct Renderer<'a, P: Pixel> {
|
||
content: &'a [Color],
|
||
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
|
||
quiet_zone: u32,
|
||
module_size: (u32, u32),
|
||
|
||
dark_color: P,
|
||
light_color: P,
|
||
has_quiet_zone: bool,
|
||
}
|
||
|
||
impl<'a, P: Pixel> Renderer<'a, P> {
|
||
/// Creates a new renderer.
|
||
pub fn new(content: &'a [Color], modules_count: usize, quiet_zone: u32) -> Renderer<'a, P> {
|
||
assert!(modules_count * modules_count == content.len());
|
||
Renderer {
|
||
content,
|
||
modules_count: modules_count.as_u32(),
|
||
quiet_zone,
|
||
module_size: P::default_unit_size(),
|
||
dark_color: P::default_color(Color::Dark),
|
||
light_color: P::default_color(Color::Light),
|
||
has_quiet_zone: true,
|
||
}
|
||
}
|
||
|
||
/// Sets color of a dark module. Default is opaque black.
|
||
pub fn dark_color(&mut self, color: P) -> &mut Self {
|
||
self.dark_color = color;
|
||
self
|
||
}
|
||
|
||
/// Sets color of a light module. Default is opaque white.
|
||
pub fn light_color(&mut self, color: P) -> &mut Self {
|
||
self.light_color = color;
|
||
self
|
||
}
|
||
|
||
/// Whether to include the quiet zone in the generated image.
|
||
pub fn quiet_zone(&mut self, has_quiet_zone: bool) -> &mut Self {
|
||
self.has_quiet_zone = has_quiet_zone;
|
||
self
|
||
}
|
||
|
||
/// Sets the size of each module in pixels. Default is 8px.
|
||
#[deprecated(since = "0.4.0", note = "use `.module_dimensions(width, width)` instead")]
|
||
pub fn module_size(&mut self, width: u32) -> &mut Self {
|
||
self.module_dimensions(width, width)
|
||
}
|
||
|
||
/// Sets the size of each module in pixels. Default is 8×8.
|
||
pub fn module_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
|
||
self.module_size = (max(width, 1), max(height, 1));
|
||
self
|
||
}
|
||
|
||
#[deprecated(since = "0.4.0", note = "use `.min_dimensions(width, width)` instead")]
|
||
pub fn min_width(&mut self, width: u32) -> &mut Self {
|
||
self.min_dimensions(width, width)
|
||
}
|
||
|
||
/// Sets the minimum total image size in pixels, including the quiet zone if
|
||
/// applicable. The renderer will try to find the dimension as small as
|
||
/// possible, such that each module in the QR code has uniform size (no
|
||
/// distortion).
|
||
///
|
||
/// For instance, a version 1 QR code has 19 modules across including the
|
||
/// quiet zone. If we request an image of size ≥200×200, we get that each
|
||
/// module's size should be 11×11, so the actual image size will be 209×209.
|
||
pub fn min_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
|
||
let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
|
||
let width_in_modules = self.modules_count + quiet_zone;
|
||
let unit_width = (width + width_in_modules - 1) / width_in_modules;
|
||
let unit_height = (height + width_in_modules - 1) / width_in_modules;
|
||
self.module_dimensions(unit_width, unit_height)
|
||
}
|
||
|
||
/// Sets the maximum total image size in pixels, including the quiet zone if
|
||
/// applicable. The renderer will try to find the dimension as large as
|
||
/// possible, such that each module in the QR code has uniform size (no
|
||
/// distortion).
|
||
///
|
||
/// For instance, a version 1 QR code has 19 modules across including the
|
||
/// quiet zone. If we request an image of size ≤200×200, we get that each
|
||
/// module's size should be 10×10, so the actual image size will be 190×190.
|
||
///
|
||
/// The module size is at least 1×1, so if the restriction is too small, the
|
||
/// final image *can* be larger than the input.
|
||
pub fn max_dimensions(&mut self, width: u32, height: u32) -> &mut Self {
|
||
let quiet_zone = if self.has_quiet_zone { 2 } else { 0 } * self.quiet_zone;
|
||
let width_in_modules = self.modules_count + quiet_zone;
|
||
let unit_width = width / width_in_modules;
|
||
let unit_height = height / width_in_modules;
|
||
self.module_dimensions(unit_width, unit_height)
|
||
}
|
||
|
||
/// Renders the QR code into an image.
|
||
#[deprecated(since = "0.4.0", note = "renamed to `.build()` to de-emphasize the image connection")]
|
||
pub fn to_image(&self) -> P::Image {
|
||
self.build()
|
||
}
|
||
|
||
/// Renders the QR code into an image.
|
||
pub fn build(&self) -> P::Image {
|
||
let w = self.modules_count;
|
||
let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 };
|
||
let width = w + 2 * qz;
|
||
|
||
let (mw, mh) = self.module_size;
|
||
let real_width = width * mw;
|
||
let real_height = width * mh;
|
||
|
||
let mut canvas = P::Canvas::new(real_width, real_height, self.dark_color, self.light_color);
|
||
let mut i = 0;
|
||
for y in 0..width {
|
||
for x in 0..width {
|
||
if qz <= x && x < w + qz && qz <= y && y < w + qz {
|
||
if self.content[i] != Color::Light {
|
||
canvas.draw_dark_rect(x * mw, y * mh, mw, mh);
|
||
}
|
||
i += 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
canvas.into_image()
|
||
}
|
||
}
|
||
|
||
//}}}
|