Merge branch 'version020'

Version 0.2.0.
This commit is contained in:
kennytm 2016-06-08 00:46:18 +08:00
commit e3c947dc99
14 changed files with 352 additions and 107 deletions

View file

@ -31,6 +31,9 @@ matrix:
- env: ARCH=i686 - env: ARCH=i686
rust: 1.8.0 rust: 1.8.0
# Note: on 1.8.0, all i686 build panics with the message "Box<Any>", cause all `should_panic` tests to fail. # Note: on 1.8.0, all i686 build panics with the message "Box<Any>", cause all `should_panic` tests to fail.
- env: ARCH=i686
rust: nightly
# Note: nightly-i686 keeps failing, it seems that travis's nightly is outdated.
install: install:
- if [ "$TRAVIS_OS_NAME" = 'linux' ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi - if [ "$TRAVIS_OS_NAME" = 'linux' ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi

View file

@ -1,21 +1,23 @@
[package] [package]
name = "qrcode" name = "qrcode"
description = "QR code encoder in Rust" description = "QR code encoder in Rust"
license = "Apache-2.0" license = "MIT / Apache-2.0"
version = "0.1.8" version = "0.2.0"
authors = ["kennytm <kennytm@gmail.com>"] authors = ["kennytm <kennytm@gmail.com>"]
keywords = ["qrcode"] keywords = ["qrcode"]
repository = "https://github.com/kennytm/qrcode-rust" repository = "https://github.com/kennytm/qrcode-rust"
readme = "README.md" readme = "README.md"
documentation = "http://kennytm.github.io/qrcode-rust/qrcode-rust/qrcode/" documentation = "http://kennytm.github.io/qrcode-rust/"
exclude = [ exclude = [
"scripts/*", ".travis.yml", ".gitignore" ".travis.yml", ".gitignore"
] ]
[dependencies] [dependencies]
num = "0.1" num-traits = "0.1.32"
image = { version = "0.10.0", optional = true }
[features] [features]
default = ["image"]
bench = [] bench = []
[[bin]] [[bin]]

19
LICENSE-MIT.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2016 kennytm
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -4,14 +4,50 @@ qrcode-rust
[![Build status](https://travis-ci.org/kennytm/qrcode-rust.svg?branch=master)](https://travis-ci.org/kennytm/qrcode-rust) [![Build status](https://travis-ci.org/kennytm/qrcode-rust.svg?branch=master)](https://travis-ci.org/kennytm/qrcode-rust)
[![Coverage Status](https://coveralls.io/repos/github/kennytm/qrcode-rust/badge.svg?branch=coveralls)](https://coveralls.io/github/kennytm/qrcode-rust?branch=coveralls) [![Coverage Status](https://coveralls.io/repos/github/kennytm/qrcode-rust/badge.svg?branch=coveralls)](https://coveralls.io/github/kennytm/qrcode-rust?branch=coveralls)
[![crates.io](http://meritbadge.herokuapp.com/qrcode)](https://crates.io/crates/qrcode) [![crates.io](http://meritbadge.herokuapp.com/qrcode)](https://crates.io/crates/qrcode)
[![Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](./LICENSE) [![MIT / Apache 2.0](https://img.shields.io/badge/license-MIT%20%2f%20Apache%202.0-blue.svg)](./LICENSE-APACHE.txt)
QR code and Micro QR code encoder in Rust. QR code and Micro QR code encoder in Rust. [Documentation](https://kennytm.github.io/qrcode-rust).
Cargo.toml
----------
```toml ```toml
# Cargo.toml
[dependencies] [dependencies]
qrcode = "0.1.7" qrcode = "0.2.0"
``` ```
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 }
```
Example
-------
This code:
```rust
extern crate qrcode;
extern crate image;
use qrcode::QrCode;
use image::GrayImage;
fn main() {
// Encode some data into bits.
let code = QrCode::new(b"01234567").unwrap();
// Render the bits into an image.
let image: GrayImage = code.render().to_image();
// Save the image.
image.save("/tmp/qrcode.png").unwrap();
}
```
Generates this image:
![Output](src/test_annex_i_qr_as_image.png)

View file

@ -894,17 +894,4 @@ mod encode_auto_tests {
//}}} //}}}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Copyright 2014 Kenny Chan
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

View file

@ -11,13 +11,13 @@
use std::iter::repeat; use std::iter::repeat;
use std::cmp::max; use std::cmp::max;
use num::traits::PrimInt;
use std::ops::Range; use std::ops::Range;
use num_traits::PrimInt;
use types::{Version, EcLevel}; use types::{Version, EcLevel};
// TODO remove this after it is decided whether we want `p ... q` or // TODO remove this after `p ... q` becomes stable. See rust-lang/rust#28237.
// `(p .. q).inclusive()`
fn range_inclusive<N: PrimInt>(from: N, to: N) -> Range<N> { fn range_inclusive<N: PrimInt>(from: N, to: N) -> Range<N> {
from .. (to + N::one()) from .. (to + N::one())
} }
@ -1884,17 +1884,4 @@ impl Canvas {
//}}} //}}}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Copyright 2014 Kenny Chan
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

View file

@ -444,17 +444,4 @@ static DATA_BYTES_PER_BLOCK: [[(usize, usize, usize, usize); 4]; 44] = [
//}}} //}}}
// Copyright 2014-2016 kennytm
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

View file

@ -2,37 +2,46 @@
//! //!
//! This crate provides a QR code and Micro QR code encoder for binary data. //! This crate provides a QR code and Micro QR code encoder for binary data.
//! //!
//! use qrcode::QrCode; //! ```
//! extern crate image;
//! extern crate qrcode;
//! //!
//! let code = QrCode::new(b"Some content here."); //! use image::GrayImage;
//! match code { //! use qrcode::QrCode;
//! Err(err) => panic!("Failed to encode the QR code: {:?}", err), //!
//! Ok(code) => { //! # fn main() {
//! for y in 0 .. code.width() { //!
//! for x in 0 .. code.width() { //! let code = QrCode::new(b"Some content here.");
//! let color = if code[(x, y)] { "black" } else { "white" }; //! match code {
//! // render color at position (x, y) //! Err(err) => panic!("Failed to encode the QR code: {:?}", err),
//! } //! Ok(code) => {
//! } //! let image: GrayImage = code.render().min_width(100).to_image();
//! } //! // render `image`...
//! } //! }
//! }
//!
//! # }
//! ```
//! //!
#![cfg_attr(feature="bench", feature(test))] // Unstable libraries #![cfg_attr(feature="bench", feature(test))] // Unstable libraries
#[cfg(feature="bench")] #[cfg(feature="bench")] extern crate test;
extern crate test; extern crate num_traits;
extern crate num; #[cfg(feature="image")] extern crate image;
use std::ops::Index; use std::ops::Index;
pub use types::{QrResult, EcLevel, Version};
pub mod types; pub mod types;
pub mod bits; pub mod bits;
pub mod optimize; pub mod optimize;
pub mod ec; pub mod ec;
pub mod canvas; pub mod canvas;
pub mod render;
pub use types::{QrResult, EcLevel, Version};
#[cfg(feature="image")] use render::{BlankAndWhitePixel, Renderer};
/// The encoded QR code symbol. /// The encoded QR code symbol.
#[derive(Clone)] #[derive(Clone)]
@ -186,6 +195,12 @@ impl QrCode {
pub fn into_vec(self) -> Vec<bool> { pub fn into_vec(self) -> Vec<bool> {
self.content self.content
} }
#[cfg(feature="image")]
pub fn render<P: BlankAndWhitePixel + 'static>(&self) -> Renderer<P> {
let quiet_zone = if self.version.is_micro() { 2 } else { 4 };
Renderer::new(&self.content, self.width, quiet_zone)
}
} }
impl Index<(usize, usize)> for QrCode { impl Index<(usize, usize)> for QrCode {
@ -249,17 +264,30 @@ mod tests {
} }
} }
// Copyright 2014 Kenny Chan #[cfg(all(test, feature="image"))]
// mod image_tests {
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use image::{GrayImage, Rgb, load_from_memory};
// use this file except in compliance with the License. You may obtain a copy of use {QrCode, Version, EcLevel};
// the License at
// #[test]
// http://www.apache.org/licenses/LICENSE-2.0 fn test_annex_i_qr_as_image() {
// let code = QrCode::new(b"01234567").unwrap();
// Unless required by applicable law or agreed to in writing, software let image: GrayImage = code.render().to_image();
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT let expected = load_from_memory(include_bytes!("test_annex_i_qr_as_image.png")).unwrap().to_luma();
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the assert_eq!(image.dimensions(), expected.dimensions());
// License for the specific language governing permissions and limitations under assert_eq!(image.into_raw(), expected.into_raw());
// the License. }
#[test]
fn test_annex_i_micro_qr_as_image() {
let code = QrCode::with_version(b"01234567", Version::Micro(2), EcLevel::L).unwrap();
let image = code.render().min_width(200)
.dark_color(Rgb { data: [128, 0, 0] })
.light_color(Rgb { data: [255, 255, 128] })
.to_image();
let expected = load_from_memory(include_bytes!("test_annex_i_micro_qr_as_image.png")).unwrap().to_rgb();
assert_eq!(image.dimensions(), expected.dimensions());
assert_eq!(image.into_raw(), expected.into_raw());
}
}

View file

@ -676,17 +676,4 @@ static STATE_TRANSITION: [(State, Action); 70] = [
//}}} //}}}
// Copyright 2014 Kenny Chan
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

222
src/render.rs Normal file
View file

@ -0,0 +1,222 @@
//! Render a QR code into image.
#![cfg(feature="image")]
use image::{Pixel, Rgb, Rgba, Luma, LumaA, Primitive, ImageBuffer};
/// A pixel which can support black and white colors.
pub trait BlankAndWhitePixel: Pixel {
fn black_color() -> Self;
fn white_color() -> Self;
}
impl<S: Primitive + 'static> BlankAndWhitePixel for Rgb<S> {
fn black_color() -> Self {
Rgb { data: [S::zero(); 3] }
}
fn white_color() -> Self {
Rgb { data: [S::max_value(); 3] }
}
}
impl<S: Primitive + 'static> BlankAndWhitePixel for Rgba<S> {
fn black_color() -> Self {
Rgba { data: [S::zero(), S::zero(), S::zero(), S::max_value()] }
}
fn white_color() -> Self {
Rgba { data: [S::max_value(); 4] }
}
}
impl<S: Primitive + 'static> BlankAndWhitePixel for Luma<S> {
fn black_color() -> Self {
Luma { data: [S::zero()] }
}
fn white_color() -> Self {
Luma { data: [S::max_value()] }
}
}
impl<S: Primitive + 'static> BlankAndWhitePixel for LumaA<S> {
fn black_color() -> Self {
LumaA { data: [S::zero(), S::max_value()] }
}
fn white_color() -> Self {
LumaA { data: [S::max_value(); 2] }
}
}
/// 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],
modules_count: u32, // <- we call it `modules_count` here to avoid ambiguity of `width`.
quiet_zone: u32,
module_size: u32,
dark_color: P,
light_color: P,
has_quiet_zone: bool,
}
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> {
assert!(modules_count * modules_count == content.len());
Renderer {
content: content,
modules_count: modules_count as u32,
quiet_zone: quiet_zone,
module_size: 8,
dark_color: P::black_color(),
light_color: P::white_color(),
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.
pub fn module_size(&mut self, size: u32) -> &mut Self {
self.module_size = size;
self
}
/// Sets the minimal total image width (and thus height) 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 width ≥200px, we get that each
/// module's size should be 11px, so the actual image size will be 209px.
pub fn min_width(&mut self, width: 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 module_size = (width + width_in_modules - 1) / width_in_modules;
self.module_size(module_size)
}
/// Renders the QR code into an image.
pub fn to_image(&self) -> ImageBuffer<P, Vec<P::Subpixel>> {
let w = self.modules_count;
let qz = if self.has_quiet_zone { self.quiet_zone } else { 0 };
let width = w + 2 * qz;
let ms = self.module_size;
let real_width = width * ms;
let mut image = ImageBuffer::new(real_width, real_width);
let mut i = 0;
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 };
i += 1;
c
} else {
self.light_color
};
for yy in y * ms .. (y + 1) * ms {
for xx in x * ms .. (x + 1) * ms {
image.put_pixel(xx, yy, color);
}
}
}
}
image
}
}
#[cfg(test)]
mod render_tests {
use render::Renderer;
use image::{Luma, Rgba};
#[test]
fn test_render_luma8_unsized() {
let image = Renderer::<Luma<u8>>::new(&[
false, true, true,
true, false, false,
false, true, false,
], 3, 1).module_size(1).to_image();
let expected = [
255, 255, 255, 255, 255,
255, 255, 0, 0, 255,
255, 0, 255, 255, 255,
255, 255, 0, 255, 255,
255, 255, 255, 255, 255,
];
assert_eq!(image.into_raw(), expected);
}
#[test]
fn test_render_rgba_unsized() {
let image = Renderer::<Rgba<u8>>::new(&[
false, true,
true, true,
], 2, 1).module_size(1).to_image();
let expected: &[u8] = &[
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 0, 0, 0,255, 255,255,255,255,
255,255,255,255, 0, 0, 0,255, 0, 0, 0,255, 255,255,255,255,
255,255,255,255, 255,255,255,255, 255,255,255,255, 255,255,255,255,
];
assert_eq!(image.into_raw(), expected);
}
#[test]
fn test_render_resized() {
let image = Renderer::<Luma<u8>>::new(&[
true, false,
false, true,
], 2, 1).min_width(10).to_image();
let expected: &[u8] = &[
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 0, 0, 0, 255,255,255, 255,255,255,
255,255,255, 0, 0, 0, 255,255,255, 255,255,255,
255,255,255, 0, 0, 0, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 0, 0, 0, 255,255,255,
255,255,255, 255,255,255, 0, 0, 0, 255,255,255,
255,255,255, 255,255,255, 0, 0, 0, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
255,255,255, 255,255,255, 255,255,255, 255,255,255,
];
assert_eq!(image.dimensions(), (12, 12));
assert_eq!(image.into_raw(), expected);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

View file

@ -279,17 +279,4 @@ mod mode_tests {
//}}} //}}}
// Copyright 2014 Kenny Chan
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.