diff --git a/README.md b/README.md index 60db9e7..59cb7bd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/bin/qrencode.rs b/src/bin/qrencode.rs index db133ca..58fd9da 100644 --- a/src/bin/qrencode.rs +++ b/src/bin/qrencode.rs @@ -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); diff --git a/src/canvas.rs b/src/canvas.rs index beacdf1..3e3cc5b 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -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(from: N, to: N) -> Range { @@ -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 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(&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(left: T, right: U) -> bool - where T: Iterator, U: Iterator, T::Item: PartialEq - { - 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 Module> = if is_horizontal { - Box::new(|k: i16| self.get(k, i)) + let get: Box 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 { self.modules.iter().map(|m| m.is_dark()).collect() } + + /// Convert the modules into a vector of colors. + pub fn into_colors(self) -> Vec { + self.modules.into_iter().map(Color::from).collect() + } } //}}} diff --git a/src/lib.rs b/src/lib.rs index 952253c..044cc22 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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, + content: Vec, 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 { - 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 { + 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 { + self.content.clone() + } + + /// Converts the QR code to a vector of colors. + pub fn into_colors(self) -> Vec { 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] } diff --git a/src/render.rs b/src/render.rs index 958a03e..bfa2a93 100644 --- a/src/render.rs +++ b/src/render.rs @@ -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 BlankAndWhitePixel for LumaA { /// 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::>::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::>::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::>::new(&[ - true, false, - false, true, + Color::Dark, Color::Light, + Color::Light, Color::Dark, ], 2, 1).min_width(10).to_image(); let expected: &[u8] = &[ diff --git a/src/types.rs b/src/types.rs index fd14b8d..1f447f1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -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 = Result; +//}}} +//------------------------------------------------------------------------------ +//{{{ 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(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