diff --git a/.travis.yml b/.travis.yml index 92e83f8..8438847 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ rust: - beta - nightly script: + - cargo build --no-default-features --features no_std -v + - cargo test --no-default-features --features no_std -v + - cargo doc --no-default-features --features no_std -v - cargo build -v - cargo test -v - cargo doc -v diff --git a/Cargo.toml b/Cargo.toml index 67b8c5b..d69e4e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "resize" version = "0.8.0" description = "Simple image resampling library in pure Rust." authors = ["Kornel ", "Kagami Hiiragi "] -categories = ["graphics", "multimedia::images"] +categories = ["graphics", "multimedia::images", "no_std"] keywords = ["resize", "scale", "resample", "image", "graphics"] documentation = "https://docs.rs/resize" homepage = "https://github.com/PistonDevelopers/resize" @@ -15,13 +15,17 @@ edition = "2021" rust-version = "1.57" [features] -default = ["rayon"] +default = ["std"] +std = ["rayon"] +no_std = ["libm", "hashbrown"] [dev-dependencies] png = "0.17.7" [dependencies] -rgb = "0.8.36" +rgb = { version = "0.8.36", default-features = false } +libm = { version = "0.2.7", optional = true } +hashbrown = { version = "0.14.0", optional = true } rayon = { version = "1.7.0", optional = true } [package.metadata.docs.rs] diff --git a/src/lib.rs b/src/lib.rs index 0f40e95..5000a75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,23 +26,43 @@ // * https://github.com/sekrit-twc/zimg/tree/master/src/zimg/resize // * https://github.com/PistonDevelopers/image/blob/master/src/imageops/sample.rs #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(any(feature = "std", feature = "no_std")))] +compile_error!("Either the `std` or `no_std` feature must be enabled"); + +extern crate alloc; + +use core::f32; +use core::fmt; +use core::num::NonZeroUsize; + +use alloc::boxed::Box; +use alloc::collections::TryReserveError; +use alloc::sync::Arc; +use alloc::vec::Vec; + +#[cfg(not(feature = "std"))] +use hashbrown::HashMap; +#[cfg(feature = "std")] use std::collections::HashMap; -use std::f32; -use std::fmt; -use std::num::NonZeroUsize; -use std::sync::Arc; #[cfg(feature = "rayon")] use rayon::prelude::*; /// See [Error] -pub type Result = std::result::Result; +pub type Result = core::result::Result; /// Pixel format from the [rgb] crate. pub mod px; pub use px::PixelFormat; +#[cfg(not(feature = "std"))] +mod no_std_float; +#[cfg(not(feature = "std"))] +#[allow(unused_imports)] +use no_std_float::FloatExt; + /// Resizing type to use. pub enum Type { /// Point resizing. @@ -152,7 +172,7 @@ fn lanczos(taps: f32, x: f32) -> f32 { #[allow(non_upper_case_globals)] pub mod Pixel { use crate::formats; - use std::marker::PhantomData; + use core::marker::PhantomData; /// Grayscale, 8-bit. #[cfg_attr(docsrs, doc(alias = "Grey"))] @@ -212,7 +232,7 @@ pub mod Pixel { /// These structs implement `PixelFormat` trait that allows conversion to and from internal pixel representation. #[doc(hidden)] pub mod formats { - use std::marker::PhantomData; + use core::marker::PhantomData; /// RGB pixels #[derive(Debug, Copy, Clone)] pub struct Rgb( @@ -441,7 +461,7 @@ impl Resizer { stride: NonZeroUsize, dst: &mut [Format::OutputPixel], ) -> Result<()> { - use std::sync::atomic::AtomicPtr; + use core::sync::atomic::AtomicPtr; let stride = stride.get(); let pix_fmt = &self.pix_fmt; @@ -471,7 +491,7 @@ impl Resizer { .enumerate() .for_each(|(x, col)| { // Acquire a safe reference to the current position in the temporary buffer. - let tmp_raw_ptr = tmp_ptr.load(std::sync::atomic::Ordering::Relaxed); + let tmp_raw_ptr = tmp_ptr.load(core::sync::atomic::Ordering::Relaxed); let mut accum = Format::new(); let in_px = &row[col.start..col.start + col.coeffs.len()]; @@ -499,7 +519,7 @@ impl Resizer { // For each pixel in the row, calculate the vertical resampling and store the result directly into the destination buffer. (0..w2).into_par_iter().for_each(|x| { // Acquire a safe reference to the current position in the temporary buffer. - let tmp_raw_ptr = tmp_ptr.load(std::sync::atomic::Ordering::Relaxed); + let tmp_raw_ptr = tmp_ptr.load(core::sync::atomic::Ordering::Relaxed); // Determine the start of the current row in the temporary buffer. let tmp_row_start = unsafe { tmp_raw_ptr.add(w2 * row.start) }; @@ -513,7 +533,7 @@ impl Resizer { } // Determine the location in the destination buffer to store the result. - let raw_ptr = dst_ptr.load(std::sync::atomic::Ordering::Relaxed); + let raw_ptr = dst_ptr.load(core::sync::atomic::Ordering::Relaxed); // Write the accumulated value to the destination buffer. unsafe { *raw_ptr.add(y * w2 + x) = pix_fmt.into_pixel(accum); @@ -625,11 +645,20 @@ pub enum Error { InvalidParameters, } +#[cfg(feature = "std")] impl std::error::Error for Error {} -impl From for Error { +impl From for Error { + #[inline(always)] + fn from(_: TryReserveError) -> Self { + Self::OutOfMemory + } +} + +#[cfg(not(feature = "std"))] +impl From for Error { #[inline(always)] - fn from(_: std::collections::TryReserveError) -> Self { + fn from(_: hashbrown::TryReserveError) -> Self { Self::OutOfMemory } } @@ -644,7 +673,7 @@ impl fmt::Display for Error { } } -#[cfg(test)] +#[cfg(all(test, feature = "std"))] mod tests { use super::*; diff --git a/src/no_std_float.rs b/src/no_std_float.rs new file mode 100644 index 0000000..19e1147 --- /dev/null +++ b/src/no_std_float.rs @@ -0,0 +1,90 @@ +/// Alternative basic float operations for no_std +pub(crate) trait FloatExt { + fn floor(self) -> Self; + fn ceil(self) -> Self; + fn sqrt(self) -> Self; + fn round(self) -> Self; + fn abs(self) -> Self; + fn trunc(self) -> Self; + fn fract(self) -> Self; + fn sin(self) -> Self; + fn powi(self, n: i32) -> Self; +} + +impl FloatExt for f32 { + #[inline] + fn floor(self) -> Self { + libm::floorf(self) + } + #[inline] + fn ceil(self) -> Self { + libm::ceilf(self) + } + #[inline] + fn sqrt(self) -> Self { + libm::sqrtf(self) + } + #[inline] + fn round(self) -> Self { + libm::roundf(self) + } + #[inline] + fn abs(self) -> Self { + libm::fabsf(self) + } + #[inline] + fn trunc(self) -> Self { + libm::truncf(self) + } + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + #[inline] + fn sin(self) -> Self { + libm::sinf(self) + } + #[inline] + fn powi(self, n: i32) -> Self { + libm::powf(self, n as _) + } +} + +impl FloatExt for f64 { + #[inline] + fn floor(self) -> Self { + libm::floor(self) + } + #[inline] + fn ceil(self) -> Self { + libm::ceil(self) + } + #[inline] + fn sqrt(self) -> Self { + libm::sqrt(self) + } + #[inline] + fn round(self) -> Self { + libm::round(self) + } + #[inline] + fn abs(self) -> Self { + libm::fabs(self) + } + #[inline] + fn trunc(self) -> Self { + libm::trunc(self) + } + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + #[inline] + fn sin(self) -> Self { + libm::sin(self) + } + #[inline] + fn powi(self, n: i32) -> Self { + libm::pow(self, n as _) + } +}