Change all bool type to explicit Color type.

This commit is contained in:
kennytm 2017-05-22 21:38:23 +08:00
parent fab0705ef8
commit 21114481f4
6 changed files with 183 additions and 128 deletions

View file

@ -13,14 +13,14 @@ Cargo.toml
```toml
[dependencies]
qrcode = "0.2.0"
qrcode = "0.4"
```
The default settings will depend on the `image` crate. If you don't need image generation capability, disable the `default-features`:
```toml
[dependencies]
qrcode = { version = "0.2.0", default-features = false }
qrcode = { version = "0.4", default-features = false }
```
Example

View file

@ -12,7 +12,7 @@ pub fn main() {
for y in 0 .. code.width() {
for x in 0 .. code.width() {
let block = if code[(x, y)] { '█' } else { SPACE };
let block = code[(x, y)].select('█', SPACE);
print!("{}{}", block, block);
}
print!("\n{}{}{}{}{}", SPACE, SPACE, SPACE, SPACE, SPACE);

View file

@ -9,13 +9,12 @@
//! c.apply_mask(MaskPattern::Checkerboard);
//! let bools = c.to_bools();
use std::iter::repeat;
use std::cmp::max;
use std::ops::Range;
use num_traits::PrimInt;
use types::{Version, EcLevel};
use types::{Version, EcLevel, Color};
// TODO remove this after `p ... q` becomes stable. See rust-lang/rust#28237.
fn range_inclusive<N: PrimInt>(from: N, to: N) -> Range<N> {
@ -31,49 +30,47 @@ pub enum Module {
/// The module is empty.
Empty,
/// The module is light (white), and cannot be masked. This mainly refers to
/// modules of functional patterns.
Light,
/// The module is of functional patterns which cannot be masked, or pixels
/// which have been masked.
Masked(Color),
/// The module is dark (black), and cannot be masked. This mainly refers to
/// modules of functional patterns.
Dark,
/// The module is of data and error correction bits before masking.
Unmasked(Color),
}
/// The module is light (white), but not yet masked. This mainly refers to
/// modules of data and error correction bits before masking.
LightUnmasked,
/// The module is dark (black), but not yet masked. This mainly refers to
/// modules of data and error correction bits before masking.
DarkUnmasked,
impl From<Module> for Color {
fn from(module: Module) -> Color {
match module {
Module::Empty => Color::Light,
Module::Masked(c) | Module::Unmasked(c) => c,
}
}
}
impl Module {
/// Checks whether a module is dark.
pub fn is_dark(&self) -> bool {
match *self {
Module::Dark | Module::DarkUnmasked => true,
_ => false,
}
pub fn is_dark(self) -> bool {
Color::from(self) == Color::Dark
}
/// Apply a mask to the unmasked modules.
///
/// use qrcode::canvas::Module;
/// use qrcode::types::Color;
///
/// assert_eq!(Module::LightUnmasked.mask(true), Module::Dark);
/// assert_eq!(Module::DarkUnmasked.mask(true), Module::Light);
/// assert_eq!(Module::LightUnmasked.mask(false), Module::Light);
/// assert_eq!(Module::Dark.mask(true), Module::Dark);
/// assert_eq!(Module::Dark.mask(false), Module::Dark);
/// assert_eq!(Module::Unmasked(Color::Light).mask(true), Module::Masked(Color::Dark));
/// assert_eq!(Module::Unmasked(Color::Dark).mask(true), Module::Masked(Color::Light));
/// assert_eq!(Module::Unmasked(Color::Light).mask(false), Module::Masked(Color::Light));
/// assert_eq!(Module::Masked(Color::Dark).mask(true), Module::Masked(Color::Dark));
/// assert_eq!(Module::Masked(Color::Dark).mask(false), Module::Masked(Color::Dark));
///
pub fn mask(&self, should_invert: bool) -> Module {
match (*self, should_invert) {
(Module::Empty, true) | (Module::LightUnmasked, true) => Module::Dark,
(Module::Empty, false) | (Module::LightUnmasked, false) => Module::Light,
(Module::DarkUnmasked, true) => Module::Light,
(Module::DarkUnmasked, false) => Module::Dark,
(a, _) => a,
pub fn mask(self, should_invert: bool) -> Module {
match (self, should_invert) {
(Module::Empty, true) => Module::Masked(Color::Dark),
(Module::Empty, false) => Module::Masked(Color::Light),
(Module::Unmasked(c), true) => Module::Masked(!c),
(Module::Unmasked(c), false) |
(Module::Masked(c), _) => Module::Masked(c),
}
}
}
@ -108,7 +105,7 @@ impl Canvas {
width: width,
version: version,
ec_level: ec_level,
modules: repeat(Module::Empty).take((width*width) as usize).collect()
modules: vec![Module::Empty; (width*width) as usize],
}
}
@ -122,10 +119,10 @@ impl Canvas {
for x in 0 .. width {
res.push(match self.get(x, y) {
Module::Empty => '?',
Module::Light => '.',
Module::Dark => '#',
Module::LightUnmasked => '-',
Module::DarkUnmasked => '*',
Module::Masked(Color::Light) => '.',
Module::Masked(Color::Dark) => '#',
Module::Unmasked(Color::Light) => '-',
Module::Unmasked(Color::Dark) => '*',
});
}
}
@ -151,18 +148,17 @@ impl Canvas {
&mut self.modules[index]
}
/// Sets the color of a module at the given coordinates. For convenience,
/// negative coordinates will wrap around.
pub fn put(&mut self, x: i16, y: i16, module: Module) {
*self.get_mut(x, y) = module;
/// Sets the color of a functional module at the given coordinates. For
/// convenience, negative coordinates will wrap around.
pub fn put(&mut self, x: i16, y: i16, color: Color) {
*self.get_mut(x, y) = Module::Masked(color);
}
}
#[cfg(test)]
mod basic_canvas_tests {
use canvas::{Canvas, Module};
use types::{Version, EcLevel};
use types::{Version, EcLevel, Color};
#[test]
fn test_index() {
@ -172,11 +168,11 @@ mod basic_canvas_tests {
assert_eq!(c.get(-1, -7), Module::Empty);
assert_eq!(c.get(21-1, 21-7), Module::Empty);
c.put(0, 0, Module::Dark);
c.put(-1, -7, Module::Light);
assert_eq!(c.get(0, 0), Module::Dark);
assert_eq!(c.get(21-1, -7), Module::Light);
assert_eq!(c.get(-1, 21-7), Module::Light);
c.put(0, 0, Color::Dark);
c.put(-1, -7, Color::Light);
assert_eq!(c.get(0, 0), Module::Masked(Color::Dark));
assert_eq!(c.get(21-1, -7), Module::Masked(Color::Light));
assert_eq!(c.get(-1, 21-7), Module::Masked(Color::Light));
}
#[test]
@ -185,14 +181,14 @@ mod basic_canvas_tests {
for i in 3i16 .. 20 {
for j in 3i16 .. 20 {
c.put(i, j, match ((i * 3) ^ j) % 5 {
*c.get_mut(i, j) = match ((i * 3) ^ j) % 5 {
0 => Module::Empty,
1 => Module::Light,
2 => Module::Dark,
3 => Module::LightUnmasked,
4 => Module::DarkUnmasked,
_ => panic!(),
});
1 => Module::Masked(Color::Light),
2 => Module::Masked(Color::Dark),
3 => Module::Unmasked(Color::Light),
4 => Module::Unmasked(Color::Dark),
_ => unreachable!(),
};
}
}
@ -233,10 +229,10 @@ impl Canvas {
for j in range_inclusive(dy_top, dy_bottom) {
for i in range_inclusive(dx_left, dx_right) {
self.put(x+i, y+j, match (i, j) {
(4, _) | (_, 4) | (-4, _) | (_, -4) => Module::Light,
(3, _) | (_, 3) | (-3, _) | (_, -3) => Module::Dark,
(2, _) | (_, 2) | (-2, _) | (_, -2) => Module::Light,
_ => Module::Dark,
(4, _) | (_, 4) | (-4, _) | (_, -4) => Color::Light,
(3, _) | (_, 3) | (-3, _) | (_, -3) => Color::Dark,
(2, _) | (_, 2) | (-2, _) | (_, -2) => Color::Light,
_ => Color::Dark,
});
}
}
@ -325,8 +321,8 @@ impl Canvas {
for j in range_inclusive(-2, 2) {
for i in range_inclusive(-2, 2) {
self.put(x+i, y+j, match (i, j) {
(2, _) | (_, 2) | (-2, _) | (_, -2) | (0, 0) => Module::Dark,
_ => Module::Light,
(2, _) | (_, 2) | (-2, _) | (_, -2) | (0, 0) => Color::Dark,
_ => Color::Light,
});
}
}
@ -534,7 +530,7 @@ impl Canvas {
/// drawn using this method.
///
fn draw_line(&mut self, x1: i16, y1: i16, x2: i16, y2: i16,
color_even: Module, color_odd: Module) {
color_even: Color, color_odd: Color) {
debug_assert!(x1 == x2 || y1 == y2);
if y1 == y2 { // Horizontal line.
@ -559,8 +555,8 @@ impl Canvas {
Version::Micro(_) => (0, 8, width-1),
Version::Normal(_) => (6, 8, width-9),
};
self.draw_line(x1, y, x2, y, Module::Dark, Module::Light);
self.draw_line(y, x1, y, x2, Module::Dark, Module::Light);
self.draw_line(x1, y, x2, y, Color::Dark, Color::Light);
self.draw_line(y, x1, y, x2, Color::Dark, Color::Light);
}
}
@ -628,7 +624,7 @@ impl Canvas {
/// iterator. It will start from the most significant bits first, so
/// *trailing* zeros will be ignored.
fn draw_number<N: PrimInt>(&mut self, number: N,
on_color: Module, off_color: Module,
on_color: Color, off_color: Color,
coords: &[(i16, i16)]) {
let zero: N = N::zero();
let mut mask: N = !(!zero >> 1);
@ -643,15 +639,15 @@ impl Canvas {
fn draw_format_info_patterns_with_number(&mut self, format_info: u16) {
match self.version {
Version::Micro(_) => {
self.draw_number(format_info, Module::Dark, Module::Light,
self.draw_number(format_info, Color::Dark, Color::Light,
&FORMAT_INFO_COORDS_MICRO_QR);
}
Version::Normal(_) => {
self.draw_number(format_info, Module::Dark, Module::Light,
self.draw_number(format_info, Color::Dark, Color::Light,
&FORMAT_INFO_COORDS_QR_MAIN);
self.draw_number(format_info, Module::Dark, Module::Light,
self.draw_number(format_info, Color::Dark, Color::Light,
&FORMAT_INFO_COORDS_QR_SIDE);
self.put(8, -8, Module::Dark); // Dark module.
self.put(8, -8, Color::Dark); // Dark module.
}
}
}
@ -667,9 +663,9 @@ impl Canvas {
Version::Micro(_) | Version::Normal(1...6) => { return; }
Version::Normal(a) => {
let version_info = VERSION_INFOS[(a - 7) as usize] << 14;
self.draw_number(version_info, Module::Dark, Module::Light,
self.draw_number(version_info, Color::Dark, Color::Light,
&VERSION_INFO_COORDS_BL);
self.draw_number(version_info, Module::Dark, Module::Light,
self.draw_number(version_info, Color::Dark, Color::Light,
&VERSION_INFO_COORDS_TR);
}
}
@ -678,13 +674,13 @@ impl Canvas {
#[cfg(test)]
mod draw_version_info_tests {
use canvas::{Canvas, Module};
use types::{Version, EcLevel};
use canvas::Canvas;
use types::{Version, EcLevel, Color};
#[test]
fn test_draw_number() {
let mut c = Canvas::new(Version::Micro(1), EcLevel::L);
c.draw_number(0b10101101u8, Module::Dark, Module::Light,
c.draw_number(0b10101101u8, Color::Dark, Color::Light,
&[(0,0), (0,-1), (-2,-2), (-2,0)]);
assert_eq!(&*c.to_debug_str(), "\n\
#????????.?\n\
@ -1284,14 +1280,14 @@ impl Canvas {
'outside:
for j in range_inclusive(bits_end, 7).rev() {
let color = if (*b & (1 << j)) != 0 {
Module::DarkUnmasked
Color::Dark
} else {
Module::LightUnmasked
Color::Light
};
while let Some((x, y)) = coords.next() {
let r = self.get_mut(x, y);
if *r == Module::Empty {
*r = color;
*r = Module::Unmasked(color);
continue 'outside;
}
}
@ -1648,35 +1644,28 @@ impl Canvas {
/// Every pattern that looks like `#.###.#....` in any orientation will add
/// 40 points.
fn compute_finder_penalty_score(&self, is_horizontal: bool) -> u16 {
static PATTERN: [Module; 7] = [
Module::Dark, Module::Light, Module::Dark, Module::Dark,
Module::Dark, Module::Light, Module::Dark,
static PATTERN: [Color; 7] = [
Color::Dark, Color::Light, Color::Dark, Color::Dark,
Color::Dark, Color::Light, Color::Dark,
];
// TODO remove this after `equals()` is stable.
fn equals<T, U>(left: T, right: U) -> bool
where T: Iterator, U: Iterator, T::Item: PartialEq<U::Item>
{
left.zip(right).all(|(p, q)| p == q)
}
let mut total_score = 0;
for i in 0 .. self.width {
for j in 0 .. self.width-6 {
// TODO a ref to a closure should be enough?
let get: Box<Fn(i16) -> Module> = if is_horizontal {
Box::new(|k: i16| self.get(k, i))
let get: Box<Fn(i16) -> Color> = if is_horizontal {
Box::new(|k| self.get(k, i).into())
} else {
Box::new(|k: i16| self.get(i, k))
Box::new(|k| self.get(i, k).into())
};
if !equals((j .. j+7).map(|k| get(k)), PATTERN.iter().map(|m| *m)) {
if (j .. j+7).map(&*get).ne(PATTERN.iter().cloned()) {
continue;
}
let check = |k| { 0 <= k && k < self.width && get(k).is_dark() };
if !(j-4 .. j).any(|k| check(k)) || !(j+7 .. j+11).any(|k| check(k)) {
let check = |k| 0 <= k && k < self.width && get(k) != Color::Light;
if !(j-4 .. j).any(&check) || !(j+7 .. j+11).any(&check) {
total_score += 40;
}
}
@ -1721,7 +1710,6 @@ impl Canvas {
/// Compute the total penalty scores. A QR code having higher points is less
/// desirable.
fn compute_total_penalty_scores(&self) -> u16 {
match self.version {
Version::Normal(_) => {
let s1a = self.compute_adjacent_penalty_score(true);
@ -1739,8 +1727,8 @@ impl Canvas {
#[cfg(test)]
mod penalty_tests {
use canvas::{Canvas, MaskPattern, Module};
use types::{Version, EcLevel};
use canvas::{Canvas, MaskPattern};
use types::{Version, EcLevel, Color};
fn create_test_canvas() -> Canvas {
let mut c = Canvas::new(Version::Normal(1), EcLevel::Q);
@ -1806,19 +1794,19 @@ mod penalty_tests {
#[test]
fn test_penalty_score_light_sides() {
static HORIZONTAL_SIDE: [Module; 17] = [
Module::Dark, Module::Light, Module::Light, Module::Dark,
Module::Dark, Module::Dark, Module::Light, Module::Light,
Module::Dark, Module::Light, Module::Dark, Module::Light,
Module::Light, Module::Dark, Module::Light, Module::Light,
Module::Light,
static HORIZONTAL_SIDE: [Color; 17] = [
Color::Dark, Color::Light, Color::Light, Color::Dark,
Color::Dark, Color::Dark, Color::Light, Color::Light,
Color::Dark, Color::Light, Color::Dark, Color::Light,
Color::Light, Color::Dark, Color::Light, Color::Light,
Color::Light,
];
static VERTICAL_SIDE: [Module; 17] = [
Module::Dark, Module::Dark, Module::Dark, Module::Light,
Module::Light, Module::Dark, Module::Dark, Module::Light,
Module::Dark, Module::Light, Module::Dark, Module::Light,
Module::Dark, Module::Light, Module::Light, Module::Dark,
Module::Light,
static VERTICAL_SIDE: [Color; 17] = [
Color::Dark, Color::Dark, Color::Dark, Color::Light,
Color::Light, Color::Dark, Color::Dark, Color::Light,
Color::Dark, Color::Light, Color::Dark, Color::Light,
Color::Dark, Color::Light, Color::Light, Color::Dark,
Color::Light,
];
let mut c = Canvas::new(Version::Micro(4), EcLevel::Q);
@ -1876,9 +1864,15 @@ impl Canvas {
}
/// Convert the modules into a vector of booleans.
#[deprecated(since="0.4.0", note="use `into_colors()` instead")]
pub fn to_bools(&self) -> Vec<bool> {
self.modules.iter().map(|m| m.is_dark()).collect()
}
/// Convert the modules into a vector of colors.
pub fn into_colors(self) -> Vec<Color> {
self.modules.into_iter().map(Color::from).collect()
}
}
//}}}

View file

@ -40,14 +40,14 @@ pub mod ec;
pub mod canvas;
pub mod render;
pub use types::{QrResult, EcLevel, Version};
pub use types::{QrResult, Color, EcLevel, Version};
#[cfg(feature="image")] use render::{BlankAndWhitePixel, Renderer};
/// The encoded QR code symbol.
#[derive(Clone)]
pub struct QrCode {
content: Vec<bool>,
content: Vec<Color>,
version: Version,
ec_level: EcLevel,
width: usize,
@ -132,7 +132,7 @@ impl QrCode {
canvas.draw_data(&*encoded_data, &*ec_data);
let canvas = canvas.apply_best_mask();
Ok(QrCode {
content: canvas.to_bools(),
content: canvas.into_colors(),
version: version,
ec_level: ec_level,
width: version.width() as usize,
@ -178,7 +178,7 @@ impl QrCode {
for _ in 0 .. width {
res.push('\n');
for _ in 0 .. width {
res.push(if self.content[k] { on_char } else { off_char });
res.push(self.content[k].select(on_char, off_char));
k += 1;
}
}
@ -187,13 +187,25 @@ impl QrCode {
/// Converts the QR code to a vector of booleans. Each entry represents the
/// color of the module, with "true" means dark and "false" means light.
#[deprecated(since="0.4.0", note="use `to_colors()` instead")]
pub fn to_vec(&self) -> Vec<bool> {
self.content.clone()
self.content.iter().map(|c| *c != Color::Light).collect()
}
/// Converts the QR code to a vector of booleans. Each entry represents the
/// color of the module, with "true" means dark and "false" means light.
#[deprecated(since="0.4.0", note="use `into_colors()` instead")]
pub fn into_vec(self) -> Vec<bool> {
self.content.into_iter().map(|c| c != Color::Light).collect()
}
/// Converts the QR code to a vector of colors.
pub fn to_colors(&self) -> Vec<Color> {
self.content.clone()
}
/// Converts the QR code to a vector of colors.
pub fn into_colors(self) -> Vec<Color> {
self.content
}
@ -231,9 +243,9 @@ impl QrCode {
}
impl Index<(usize, usize)> for QrCode {
type Output = bool;
type Output = Color;
fn index(&self, (x, y): (usize, usize)) -> &bool {
fn index(&self, (x, y): (usize, usize)) -> &Color {
let index = y * self.width + x;
&self.content[index]
}

View file

@ -3,6 +3,7 @@
#![cfg(feature="image")]
use image::{Pixel, Rgb, Rgba, Luma, LumaA, Primitive, ImageBuffer};
use types::Color;
/// A pixel which can support black and white colors.
pub trait BlankAndWhitePixel: Pixel {
@ -53,7 +54,7 @@ impl<S: Primitive + 'static> BlankAndWhitePixel for LumaA<S> {
/// A QR code renderer. This is a builder type which converts a bool-vector into
/// an image.
pub struct Renderer<'a, P: BlankAndWhitePixel> {
content: &'a [bool],
content: &'a [Color],
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
quiet_zone: u32,
module_size: u32,
@ -65,7 +66,7 @@ pub struct Renderer<'a, P: BlankAndWhitePixel> {
impl<'a, P: BlankAndWhitePixel + 'static> Renderer<'a, P> {
/// Creates a new renderer.
pub fn new(content: &'a [bool], modules_count: usize, quiet_zone: u32) -> Renderer<'a, P> {
pub fn new(content: &'a [Color], modules_count: usize, quiet_zone: u32) -> Renderer<'a, P> {
assert!(modules_count * modules_count == content.len());
Renderer {
content: content,
@ -131,7 +132,11 @@ impl<'a, P: BlankAndWhitePixel + 'static> Renderer<'a, P> {
for y in 0 .. width {
for x in 0 .. width {
let color = if qz <= x && x < w + qz && qz <= y && y < w + qz {
let c = if self.content[i] { self.dark_color } else { self.light_color };
let c = if self.content[i] != Color::Light {
self.dark_color
} else {
self.light_color
};
i += 1;
c
} else {
@ -153,13 +158,14 @@ impl<'a, P: BlankAndWhitePixel + 'static> Renderer<'a, P> {
mod render_tests {
use render::Renderer;
use image::{Luma, Rgba};
use types::Color;
#[test]
fn test_render_luma8_unsized() {
let image = Renderer::<Luma<u8>>::new(&[
false, true, true,
true, false, false,
false, true, false,
Color::Light, Color::Dark, Color::Dark,
Color::Dark, Color::Light, Color::Light,
Color::Light, Color::Dark, Color::Light,
], 3, 1).module_size(1).to_image();
let expected = [
@ -175,8 +181,8 @@ mod render_tests {
#[test]
fn test_render_rgba_unsized() {
let image = Renderer::<Rgba<u8>>::new(&[
false, true,
true, true,
Color::Light, Color::Dark,
Color::Dark, Color::Dark,
], 2, 1).module_size(1).to_image();
let expected: &[u8] = &[
@ -192,8 +198,8 @@ mod render_tests {
#[test]
fn test_render_resized() {
let image = Renderer::<Luma<u8>>::new(&[
true, false,
false, true,
Color::Dark, Color::Light,
Color::Light, Color::Dark,
], 2, 1).min_width(10).to_image();
let expected: &[u8] = &[

View file

@ -1,6 +1,7 @@
use std::default::Default;
use std::cmp::{PartialOrd, Ordering};
use std::fmt::{Display, Formatter, Error};
use std::ops::Not;
//------------------------------------------------------------------------------
//{{{ QrResult
@ -42,6 +43,48 @@ impl Display for QrError {
/// `QrResult` is a convenient alias for a QR code generation result.
pub type QrResult<T> = Result<T, QrError>;
//}}}
//------------------------------------------------------------------------------
//{{{ Color
/// The color of a module.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Color {
/// The module is light colored.
Light,
/// The module is dark colored.
Dark,
}
impl Color {
/// Selects a value according to color of the module. Equivalent to
/// `if self != Color::Light { dark } else { light }`.
///
/// # Examples
///
/// ```rust
/// # use qrcode::types::Color;
/// assert_eq!(Color::Light.select(1, 0), 0);
/// assert_eq!(Color::Dark.select("black", "white"), "black");
/// ```
pub fn select<T>(self, dark: T, light: T) -> T {
match self {
Color::Light => light,
Color::Dark => dark,
}
}
}
impl Not for Color {
type Output = Color;
fn not(self) -> Color {
match self {
Color::Light => Color::Dark,
Color::Dark => Color::Light,
}
}
}
//}}}
//------------------------------------------------------------------------------
//{{{ Error correction level