Move Canvas and QRCode to iced crate
Rename `canvas` modules to `geometry` in graphics subcrates
This commit is contained in:
parent
d13d19ba35
commit
6cc48b5c62
39 changed files with 140 additions and 148 deletions
|
|
@ -6,8 +6,7 @@ edition = "2021"
|
|||
[features]
|
||||
image = ["iced_wgpu/image", "iced_tiny_skia/image"]
|
||||
svg = ["iced_wgpu/svg", "iced_tiny_skia/svg"]
|
||||
canvas = ["iced_wgpu/canvas", "iced_tiny_skia/canvas"]
|
||||
qr_code = ["canvas", "qrcode"]
|
||||
geometry = ["iced_wgpu/geometry", "iced_tiny_skia/geometry"]
|
||||
tracing = ["iced_wgpu/tracing"]
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -31,8 +30,3 @@ iced_wgpu = { version = "0.9", path = "../wgpu" }
|
|||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
iced_wgpu = { version = "0.9", path = "../wgpu", features = ["webgl"] }
|
||||
|
||||
[dependencies.qrcode]
|
||||
version = "0.12"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{Font, Geometry, Point, Size};
|
||||
use crate::{Font, Point, Size};
|
||||
|
||||
use iced_graphics::backend;
|
||||
use iced_graphics::text;
|
||||
|
|
@ -12,8 +12,6 @@ pub enum Backend {
|
|||
}
|
||||
|
||||
impl iced_graphics::Backend for Backend {
|
||||
type Geometry = Geometry;
|
||||
|
||||
fn trim_measurements(&mut self) {
|
||||
match self {
|
||||
Self::Wgpu(backend) => backend.trim_measurements(),
|
||||
|
|
|
|||
|
|
@ -2,22 +2,13 @@ mod cache;
|
|||
|
||||
pub use cache::Cache;
|
||||
|
||||
pub use iced_native::widget::canvas::event::{self, Event};
|
||||
pub use iced_native::widget::canvas::fill::{self, Fill};
|
||||
pub use iced_native::widget::canvas::gradient::{self, Gradient};
|
||||
pub use iced_native::widget::canvas::path::{self, Path};
|
||||
pub use iced_native::widget::canvas::stroke::{self, Stroke};
|
||||
pub use iced_native::widget::canvas::{
|
||||
Canvas, Cursor, LineCap, LineDash, LineJoin, Program, Renderer, Style, Text,
|
||||
};
|
||||
pub use iced_graphics::geometry::*;
|
||||
|
||||
use crate::{Backend, Point, Rectangle, Size, Vector};
|
||||
|
||||
pub use crate::Geometry;
|
||||
|
||||
pub enum Frame {
|
||||
Wgpu(iced_wgpu::canvas::Frame),
|
||||
TinySkia(iced_tiny_skia::canvas::Frame),
|
||||
Wgpu(iced_wgpu::geometry::Frame),
|
||||
TinySkia(iced_tiny_skia::geometry::Frame),
|
||||
}
|
||||
|
||||
macro_rules! delegate {
|
||||
|
|
@ -33,10 +24,10 @@ impl Frame {
|
|||
pub fn new<Theme>(renderer: &crate::Renderer<Theme>, size: Size) -> Self {
|
||||
match renderer.backend() {
|
||||
Backend::Wgpu(_) => {
|
||||
Frame::Wgpu(iced_wgpu::canvas::Frame::new(size))
|
||||
Frame::Wgpu(iced_wgpu::geometry::Frame::new(size))
|
||||
}
|
||||
Backend::TinySkia(_) => {
|
||||
Frame::TinySkia(iced_tiny_skia::canvas::Frame::new(size))
|
||||
Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -131,10 +122,10 @@ impl Frame {
|
|||
pub fn with_clip(&mut self, region: Rectangle, f: impl FnOnce(&mut Frame)) {
|
||||
let mut frame = match self {
|
||||
Self::Wgpu(_) => {
|
||||
Self::Wgpu(iced_wgpu::canvas::Frame::new(region.size()))
|
||||
Self::Wgpu(iced_wgpu::geometry::Frame::new(region.size()))
|
||||
}
|
||||
Self::TinySkia(_) => Self::TinySkia(
|
||||
iced_tiny_skia::canvas::Frame::new(region.size()),
|
||||
iced_tiny_skia::geometry::Frame::new(region.size()),
|
||||
),
|
||||
};
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::widget::canvas::{Frame, Geometry};
|
||||
use crate::geometry::{Frame, Geometry};
|
||||
use crate::{Primitive, Renderer, Size};
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
pub mod widget;
|
||||
pub mod window;
|
||||
|
||||
#[cfg(feature = "geometry")]
|
||||
pub mod geometry;
|
||||
|
||||
mod backend;
|
||||
mod settings;
|
||||
|
||||
|
|
@ -19,12 +21,3 @@ pub use iced_graphics::{
|
|||
/// [`iced`]: https://github.com/iced-rs/iced
|
||||
pub type Renderer<Theme = iced_native::Theme> =
|
||||
iced_graphics::Renderer<Backend, Theme>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Geometry(pub(crate) Primitive);
|
||||
|
||||
impl From<Geometry> for Primitive {
|
||||
fn from(geometry: Geometry) -> Self {
|
||||
geometry.0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,301 +0,0 @@
|
|||
//! Encode and display information in a QR code.
|
||||
use crate::widget::canvas;
|
||||
use crate::Renderer;
|
||||
|
||||
use iced_graphics::renderer;
|
||||
|
||||
use iced_native::layout;
|
||||
use iced_native::widget::Tree;
|
||||
use iced_native::{
|
||||
Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
const DEFAULT_CELL_SIZE: u16 = 4;
|
||||
const QUIET_ZONE: usize = 2;
|
||||
|
||||
/// A type of matrix barcode consisting of squares arranged in a grid which
|
||||
/// can be read by an imaging device, such as a camera.
|
||||
#[derive(Debug)]
|
||||
pub struct QRCode<'a> {
|
||||
state: &'a State,
|
||||
dark: Color,
|
||||
light: Color,
|
||||
cell_size: u16,
|
||||
}
|
||||
|
||||
impl<'a> QRCode<'a> {
|
||||
/// Creates a new [`QRCode`] with the provided [`State`].
|
||||
pub fn new(state: &'a State) -> Self {
|
||||
Self {
|
||||
cell_size: DEFAULT_CELL_SIZE,
|
||||
dark: Color::BLACK,
|
||||
light: Color::WHITE,
|
||||
state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets both the dark and light [`Color`]s of the [`QRCode`].
|
||||
pub fn color(mut self, dark: Color, light: Color) -> Self {
|
||||
self.dark = dark;
|
||||
self.light = light;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the size of the squares of the grid cell of the [`QRCode`].
|
||||
pub fn cell_size(mut self, cell_size: u16) -> Self {
|
||||
self.cell_size = cell_size;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {
|
||||
fn width(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
_renderer: &Renderer<Theme>,
|
||||
_limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let side_length = (self.state.width + 2 * QUIET_ZONE) as f32
|
||||
* f32::from(self.cell_size);
|
||||
|
||||
layout::Node::new(Size::new(side_length, side_length))
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Tree,
|
||||
renderer: &mut Renderer<Theme>,
|
||||
_theme: &Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
_cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
use iced_native::Renderer as _;
|
||||
|
||||
let bounds = layout.bounds();
|
||||
let side_length = self.state.width + 2 * QUIET_ZONE;
|
||||
|
||||
// Reuse cache if possible
|
||||
let geometry =
|
||||
self.state.cache.draw(renderer, bounds.size(), |frame| {
|
||||
// Scale units to cell size
|
||||
frame.scale(f32::from(self.cell_size));
|
||||
|
||||
// Draw background
|
||||
frame.fill_rectangle(
|
||||
Point::ORIGIN,
|
||||
Size::new(side_length as f32, side_length as f32),
|
||||
self.light,
|
||||
);
|
||||
|
||||
// Avoid drawing on the quiet zone
|
||||
frame.translate(Vector::new(
|
||||
QUIET_ZONE as f32,
|
||||
QUIET_ZONE as f32,
|
||||
));
|
||||
|
||||
// Draw contents
|
||||
self.state
|
||||
.contents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, value)| **value == qrcode::Color::Dark)
|
||||
.for_each(|(index, _)| {
|
||||
let row = index / self.state.width;
|
||||
let column = index % self.state.width;
|
||||
|
||||
frame.fill_rectangle(
|
||||
Point::new(column as f32, row as f32),
|
||||
Size::UNIT,
|
||||
self.dark,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
let translation = Vector::new(bounds.x, bounds.y);
|
||||
|
||||
renderer.with_translation(translation, |renderer| {
|
||||
renderer.draw_primitive(geometry.0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme> From<QRCode<'a>>
|
||||
for Element<'a, Message, Renderer<Theme>>
|
||||
{
|
||||
fn from(qr_code: QRCode<'a>) -> Self {
|
||||
Self::new(qr_code)
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of a [`QRCode`].
|
||||
///
|
||||
/// It stores the data that will be displayed.
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
contents: Vec<qrcode::Color>,
|
||||
width: usize,
|
||||
cache: canvas::Cache,
|
||||
}
|
||||
|
||||
impl State {
|
||||
/// Creates a new [`State`] with the provided data.
|
||||
///
|
||||
/// This method uses an [`ErrorCorrection::Medium`] and chooses the smallest
|
||||
/// size to display the data.
|
||||
pub fn new(data: impl AsRef<[u8]>) -> Result<Self, Error> {
|
||||
let encoded = qrcode::QrCode::new(data)?;
|
||||
|
||||
Ok(Self::build(encoded))
|
||||
}
|
||||
|
||||
/// Creates a new [`State`] with the provided [`ErrorCorrection`].
|
||||
pub fn with_error_correction(
|
||||
data: impl AsRef<[u8]>,
|
||||
error_correction: ErrorCorrection,
|
||||
) -> Result<Self, Error> {
|
||||
let encoded = qrcode::QrCode::with_error_correction_level(
|
||||
data,
|
||||
error_correction.into(),
|
||||
)?;
|
||||
|
||||
Ok(Self::build(encoded))
|
||||
}
|
||||
|
||||
/// Creates a new [`State`] with the provided [`Version`] and
|
||||
/// [`ErrorCorrection`].
|
||||
pub fn with_version(
|
||||
data: impl AsRef<[u8]>,
|
||||
version: Version,
|
||||
error_correction: ErrorCorrection,
|
||||
) -> Result<Self, Error> {
|
||||
let encoded = qrcode::QrCode::with_version(
|
||||
data,
|
||||
version.into(),
|
||||
error_correction.into(),
|
||||
)?;
|
||||
|
||||
Ok(Self::build(encoded))
|
||||
}
|
||||
|
||||
fn build(encoded: qrcode::QrCode) -> Self {
|
||||
let width = encoded.width();
|
||||
let contents = encoded.into_colors();
|
||||
|
||||
Self {
|
||||
contents,
|
||||
width,
|
||||
cache: canvas::Cache::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// The size of a [`QRCode`].
|
||||
///
|
||||
/// The higher the version the larger the grid of cells, and therefore the more
|
||||
/// information the [`QRCode`] can carry.
|
||||
pub enum Version {
|
||||
/// A normal QR code version. It should be between 1 and 40.
|
||||
Normal(u8),
|
||||
|
||||
/// A micro QR code version. It should be between 1 and 4.
|
||||
Micro(u8),
|
||||
}
|
||||
|
||||
impl From<Version> for qrcode::Version {
|
||||
fn from(version: Version) -> Self {
|
||||
match version {
|
||||
Version::Normal(v) => qrcode::Version::Normal(i16::from(v)),
|
||||
Version::Micro(v) => qrcode::Version::Micro(i16::from(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The error correction level.
|
||||
///
|
||||
/// It controls the amount of data that can be damaged while still being able
|
||||
/// to recover the original information.
|
||||
///
|
||||
/// A higher error correction level allows for more corrupted data.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ErrorCorrection {
|
||||
/// Low error correction. 7% of the data can be restored.
|
||||
Low,
|
||||
/// Medium error correction. 15% of the data can be restored.
|
||||
Medium,
|
||||
/// Quartile error correction. 25% of the data can be restored.
|
||||
Quartile,
|
||||
/// High error correction. 30% of the data can be restored.
|
||||
High,
|
||||
}
|
||||
|
||||
impl From<ErrorCorrection> for qrcode::EcLevel {
|
||||
fn from(ec_level: ErrorCorrection) -> Self {
|
||||
match ec_level {
|
||||
ErrorCorrection::Low => qrcode::EcLevel::L,
|
||||
ErrorCorrection::Medium => qrcode::EcLevel::M,
|
||||
ErrorCorrection::Quartile => qrcode::EcLevel::Q,
|
||||
ErrorCorrection::High => qrcode::EcLevel::H,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that occurred when building a [`State`] for a [`QRCode`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
|
||||
pub enum Error {
|
||||
/// The data is too long to encode in a QR code for the chosen [`Version`].
|
||||
#[error(
|
||||
"The data is too long to encode in a QR code for the chosen version"
|
||||
)]
|
||||
DataTooLong,
|
||||
|
||||
/// The chosen [`Version`] and [`ErrorCorrection`] combination is invalid.
|
||||
#[error(
|
||||
"The chosen version and error correction level combination is invalid."
|
||||
)]
|
||||
InvalidVersion,
|
||||
|
||||
/// One or more characters in the provided data are not supported by the
|
||||
/// chosen [`Version`].
|
||||
#[error(
|
||||
"One or more characters in the provided data are not supported by the \
|
||||
chosen version"
|
||||
)]
|
||||
UnsupportedCharacterSet,
|
||||
|
||||
/// The chosen ECI designator is invalid. A valid designator should be
|
||||
/// between 0 and 999999.
|
||||
#[error(
|
||||
"The chosen ECI designator is invalid. A valid designator should be \
|
||||
between 0 and 999999."
|
||||
)]
|
||||
InvalidEciDesignator,
|
||||
|
||||
/// A character that does not belong to the character set was found.
|
||||
#[error("A character that does not belong to the character set was found")]
|
||||
InvalidCharacter,
|
||||
}
|
||||
|
||||
impl From<qrcode::types::QrError> for Error {
|
||||
fn from(error: qrcode::types::QrError) -> Self {
|
||||
use qrcode::types::QrError;
|
||||
|
||||
match error {
|
||||
QrError::DataTooLong => Error::DataTooLong,
|
||||
QrError::InvalidVersion => Error::InvalidVersion,
|
||||
QrError::UnsupportedCharacterSet => Error::UnsupportedCharacterSet,
|
||||
QrError::InvalidEciDesignator => Error::InvalidEciDesignator,
|
||||
QrError::InvalidCharacter => Error::InvalidCharacter,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue