Move Canvas and QRCode to iced crate

Rename `canvas` modules to `geometry` in graphics subcrates
This commit is contained in:
Héctor Ramón Jiménez 2023-03-03 04:57:55 +01:00
parent d13d19ba35
commit 6cc48b5c62
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
39 changed files with 140 additions and 148 deletions

View file

@ -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

View file

@ -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(),

View file

@ -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()),
),
};

View file

@ -1,4 +1,4 @@
use crate::widget::canvas::{Frame, Geometry};
use crate::geometry::{Frame, Geometry};
use crate::{Primitive, Renderer, Size};
use std::cell::RefCell;

View file

@ -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
}
}

View file

@ -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,
}
}
}