From bf351495ccb8c1d18c3b8e88b7853d098dc4bfd2 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Tue, 5 Dec 2023 10:04:15 -0600 Subject: [PATCH 1/3] Support for no-alloc --- Cargo.toml | 7 ++++--- src/bezpath.rs | 14 ++++++++++++++ src/cubicbez.rs | 7 ++++++- src/lib.rs | 14 ++++++++++++-- src/shape.rs | 10 +++++++++- 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b1c4daad..134db49f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,9 @@ categories = ["graphics"] features = ["mint", "schemars", "serde"] [features] -default = ["std"] -std = [] +default = ["std", "alloc"] +std = ["alloc"] +alloc = ["serde/alloc"] [dependencies] smallvec = "1.10" @@ -41,7 +42,7 @@ optional = true version = "1.0.105" optional = true default-features = false -features = ["alloc", "derive"] +features = ["derive"] # This is used for research but not really needed; maybe refactor. [dev-dependencies] diff --git a/src/bezpath.rs b/src/bezpath.rs index eddd03e7..1f0c0a76 100644 --- a/src/bezpath.rs +++ b/src/bezpath.rs @@ -103,6 +103,7 @@ use crate::common::FloatFuncs; #[derive(Clone, Default, Debug, PartialEq)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg(feature = "alloc")] pub struct BezPath(Vec); /// The element of a Bézier path. @@ -173,6 +174,7 @@ pub struct MinDistance { pub t2: f64, } +#[cfg(feature = "alloc")] impl BezPath { /// Create a new path. pub fn new() -> BezPath { @@ -488,6 +490,7 @@ impl BezPath { /// Helper for reversing a subpath. /// /// The `els` parameter must not contain any `MoveTo` or `ClosePath` elements. +#[cfg(feature = "alloc")] fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) { let end_pt = els.last().and_then(|el| el.end_point()).unwrap_or(start_pt); reversed.push(PathEl::MoveTo(end_pt)); @@ -506,6 +509,7 @@ fn reverse_subpath(start_pt: Point, els: &[PathEl], reversed: &mut BezPath) { } } +#[cfg(feature = "alloc")] impl FromIterator for BezPath { fn from_iter>(iter: T) -> Self { let el_vec: Vec<_> = iter.into_iter().collect(); @@ -517,6 +521,7 @@ impl FromIterator for BezPath { /// /// Note: the semantics are slightly different from simply iterating over the /// slice, as it returns `PathEl` items, rather than references. +#[cfg(feature = "alloc")] impl<'a> IntoIterator for &'a BezPath { type Item = PathEl; type IntoIter = core::iter::Cloned>; @@ -526,6 +531,7 @@ impl<'a> IntoIterator for &'a BezPath { } } +#[cfg(feature = "alloc")] impl IntoIterator for BezPath { type Item = PathEl; type IntoIter = alloc::vec::IntoIter; @@ -535,6 +541,7 @@ impl IntoIterator for BezPath { } } +#[cfg(feature = "alloc")] impl Extend for BezPath { fn extend>(&mut self, iter: I) { self.0.extend(iter); @@ -662,6 +669,7 @@ impl Mul for Affine { } } +#[cfg(feature = "alloc")] impl Mul for Affine { type Output = BezPath; @@ -670,6 +678,7 @@ impl Mul for Affine { } } +#[cfg(feature = "alloc")] impl<'a> Mul<&'a BezPath> for Affine { type Output = BezPath; @@ -704,6 +713,7 @@ impl Mul for TranslateScale { } } +#[cfg(feature = "alloc")] impl Mul for TranslateScale { type Output = BezPath; @@ -712,6 +722,7 @@ impl Mul for TranslateScale { } } +#[cfg(feature = "alloc")] impl<'a> Mul<&'a BezPath> for TranslateScale { type Output = BezPath; @@ -1277,6 +1288,7 @@ impl From for PathSeg { } } +#[cfg(feature = "alloc")] impl Shape for BezPath { type PathElementsIter<'iter> = core::iter::Copied>; @@ -1366,6 +1378,7 @@ impl<'a> Shape for &'a [PathEl] { self.iter().copied() } + #[cfg(feature = "alloc")] fn to_path(&self, _tolerance: f64) -> BezPath { BezPath::from_vec(self.to_vec()) } @@ -1406,6 +1419,7 @@ impl Shape for [PathEl; N] { self.iter().copied() } + #[cfg(feature = "alloc")] fn to_path(&self, _tolerance: f64) -> BezPath { BezPath::from_vec(self.to_vec()) } diff --git a/src/cubicbez.rs b/src/cubicbez.rs index 3a59f8f3..65daff47 100644 --- a/src/cubicbez.rs +++ b/src/cubicbez.rs @@ -3,8 +3,10 @@ //! Cubic Bézier segments. -use alloc::vec; +#[cfg(feature = "alloc")] use alloc::vec::Vec; +#[cfg(feature = "alloc")] +use alloc::vec; use core::ops::{Mul, Range}; use crate::MAX_EXTREMA; @@ -100,11 +102,13 @@ impl CubicBez { /// /// Returns `None` if no suitable approximation is found within the given /// tolerance. + #[cfg(feature = "alloc")] pub fn approx_spline(&self, accuracy: f64) -> Option { (1..=MAX_SPLINE_SPLIT).find_map(|n| self.approx_spline_n(n, accuracy)) } // Approximate a cubic curve with a quadratic spline of `n` curves + #[cfg(feature = "alloc")] fn approx_spline_n(&self, n: usize, accuracy: f64) -> Option { if n == 1 { return self @@ -734,6 +738,7 @@ impl Iterator for ToQuads { /// Ensures that the resulting splines have the same number of control points. /// /// Rust port of cu2qu [cubic_approx_quadratic](https://github.com/fonttools/fonttools/blob/3b9a73ff8379ab49d3ce35aaaaf04b3a7d9d1655/Lib/fontTools/cu2qu/cu2qu.py#L322) +#[cfg(feature = "alloc")] pub fn cubics_to_quadratic_splines(curves: &[CubicBez], accuracy: f64) -> Option> { let mut result = Vec::new(); let mut split_order = 0; diff --git a/src/lib.rs b/src/lib.rs index 3c5563a8..b9d984b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,8 +61,11 @@ //! math functionality. The `std` feature is enabled by default, but can be //! disabled, as long as the `libm` feature is enabled. This is useful for //! `no_std` environments. However, note that the `libm` crate is not as -//! efficient as the standard library, and that this crate still uses the -//! `alloc` crate regardless. +//! efficient as the standard library. +//! +//! The `alloc` feature (which is enabled by default) may be turned off to +//! disable all allocations. This removes support for [`BezPath`] and all +//! features (including curve fitting and offsets) that depend on it. //! //! [`Piet`]: https://docs.rs/piet //! [`Druid`]: https://docs.rs/druid @@ -78,6 +81,7 @@ clippy::bool_to_int_with_if )] #![cfg_attr(all(not(feature = "std"), not(test)), no_std)] +#![cfg_attr(not(feature = "alloc"), allow(dead_code, unused_imports))] #[cfg(not(any(feature = "std", feature = "libm")))] compile_error!("kurbo requires either the `std` or `libm` feature"); @@ -91,10 +95,12 @@ mod circle; pub mod common; mod cubicbez; mod ellipse; +#[cfg(feature = "alloc")] mod fit; mod insets; mod line; mod mindist; +#[cfg(feature = "alloc")] pub mod offset; mod param_curve; mod point; @@ -104,8 +110,10 @@ mod rect; mod rounded_rect; mod rounded_rect_radii; mod shape; +#[cfg(feature = "alloc")] pub mod simplify; mod size; +#[cfg(feature = "alloc")] mod stroke; #[cfg(feature = "std")] mod svg; @@ -118,6 +126,7 @@ pub use crate::bezpath::*; pub use crate::circle::*; pub use crate::cubicbez::*; pub use crate::ellipse::*; +#[cfg(feature = "alloc")] pub use crate::fit::*; pub use crate::insets::*; pub use crate::line::*; @@ -130,6 +139,7 @@ pub use crate::rounded_rect::*; pub use crate::rounded_rect_radii::*; pub use crate::shape::*; pub use crate::size::*; +#[cfg(feature = "alloc")] pub use crate::stroke::*; #[cfg(feature = "std")] pub use crate::svg::*; diff --git a/src/shape.rs b/src/shape.rs index c850dab9..763a65a5 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -3,7 +3,10 @@ //! A generic trait for shapes. -use crate::{segments, BezPath, Circle, Line, PathEl, Point, Rect, RoundedRect, Segments}; +use crate::{segments, Circle, Line, PathEl, Point, Rect, RoundedRect, Segments}; + +#[cfg(feature = "alloc")] +use crate::BezPath; /// A generic trait for open and closed shapes. /// @@ -64,12 +67,14 @@ pub trait Shape: Sized { /// The `tolerance` parameter is the same as for [`path_elements`]. /// /// [`path_elements`]: Shape::path_elements + #[cfg(feature = "alloc")] fn to_path(&self, tolerance: f64) -> BezPath { self.path_elements(tolerance).collect() } #[deprecated(since = "0.7.0", note = "Use path_elements instead")] #[doc(hidden)] + #[cfg(feature = "alloc")] fn to_bez_path(&self, tolerance: f64) -> Self::PathElementsIter<'_> { self.path_elements(tolerance) } @@ -81,12 +86,14 @@ pub trait Shape: Sized { /// The `tolerance` parameter is the same as for [`path_elements()`]. /// /// [`path_elements()`]: Shape::path_elements + #[cfg(feature = "alloc")] fn into_path(self, tolerance: f64) -> BezPath { self.to_path(tolerance) } #[deprecated(since = "0.7.0", note = "Use into_path instead")] #[doc(hidden)] + #[cfg(feature = "alloc")] fn into_bez_path(self, tolerance: f64) -> BezPath { self.into_path(tolerance) } @@ -181,6 +188,7 @@ impl<'a, T: Shape> Shape for &'a T { (*self).path_elements(tolerance) } + #[cfg(feature = "alloc")] fn to_path(&self, tolerance: f64) -> BezPath { (*self).to_path(tolerance) } From d54a65771cb339984c2372cfb7aed3c344f88a85 Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Tue, 5 Dec 2023 10:14:16 -0600 Subject: [PATCH 2/3] cargo fmt, and handle smallvec --- Cargo.toml | 7 ++++--- src/cubicbez.rs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 134db49f..d976d8a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,11 @@ features = ["mint", "schemars", "serde"] [features] default = ["std", "alloc"] std = ["alloc"] -alloc = ["serde/alloc"] +alloc = ["serde/alloc", "smallvec"] -[dependencies] -smallvec = "1.10" +[dependencies.smallvec] +version = "1.10" +optional = true [dependencies.arrayvec] version = "0.7.1" diff --git a/src/cubicbez.rs b/src/cubicbez.rs index 65daff47..70b276bb 100644 --- a/src/cubicbez.rs +++ b/src/cubicbez.rs @@ -3,10 +3,10 @@ //! Cubic Bézier segments. -#[cfg(feature = "alloc")] -use alloc::vec::Vec; #[cfg(feature = "alloc")] use alloc::vec; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use core::ops::{Mul, Range}; use crate::MAX_EXTREMA; From 7f3213133b2d079e0d5fc561bbac6704782e0fba Mon Sep 17 00:00:00 2001 From: Joe Neeman Date: Tue, 5 Dec 2023 22:28:08 -0600 Subject: [PATCH 3/3] Missed a few --- src/bezpath.rs | 2 ++ src/cubicbez.rs | 7 +++++-- src/lib.rs | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/bezpath.rs b/src/bezpath.rs index 1f0c0a76..aa713730 100644 --- a/src/bezpath.rs +++ b/src/bezpath.rs @@ -9,6 +9,7 @@ use core::iter::{Extend, FromIterator}; use core::mem; use core::ops::{Mul, Range}; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use arrayvec::ArrayVec; @@ -556,6 +557,7 @@ const TO_QUAD_TOL: f64 = 0.1; /// See [`BezPath::flatten`] for more discussion. /// This signature is a bit more general, allowing flattening of `&[PathEl]` slices /// and other iterators yielding `PathEl`. +#[cfg(feature = "alloc")] pub fn flatten( path: impl IntoIterator, tolerance: f64, diff --git a/src/cubicbez.rs b/src/cubicbez.rs index 70b276bb..c817e2cf 100644 --- a/src/cubicbez.rs +++ b/src/cubicbez.rs @@ -9,8 +9,11 @@ use alloc::vec; use alloc::vec::Vec; use core::ops::{Mul, Range}; +#[cfg(feature = "alloc")] +use crate::QuadSpline; + use crate::MAX_EXTREMA; -use crate::{Line, QuadSpline, Vec2}; +use crate::{Line, Vec2}; use arrayvec::ArrayVec; use crate::common::{ @@ -691,7 +694,7 @@ impl ParamCurveExtrema for CubicBez { let d2 = self.p3 - self.p2; one_coord(&mut result, d0.x, d1.x, d2.x); one_coord(&mut result, d0.y, d1.y, d2.y); - result.sort_by(|a, b| a.partial_cmp(b).unwrap()); + result.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); result } } diff --git a/src/lib.rs b/src/lib.rs index b9d984b9..3841062a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,7 @@ #[cfg(not(any(feature = "std", feature = "libm")))] compile_error!("kurbo requires either the `std` or `libm` feature"); +#[cfg(feature = "alloc")] extern crate alloc; mod affine; @@ -105,6 +106,7 @@ pub mod offset; mod param_curve; mod point; mod quadbez; +#[cfg(feature = "alloc")] mod quadspline; mod rect; mod rounded_rect; @@ -133,6 +135,7 @@ pub use crate::line::*; pub use crate::param_curve::*; pub use crate::point::*; pub use crate::quadbez::*; +#[cfg(feature = "alloc")] pub use crate::quadspline::*; pub use crate::rect::*; pub use crate::rounded_rect::*;