Skip to content

Commit

Permalink
Merge pull request #35 from EHfive/to_no_std
Browse files Browse the repository at this point in the history
Make crate no_std
  • Loading branch information
kornelski authored Sep 20, 2023
2 parents 843c3a9 + c3ed8d9 commit 46b63be
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 87 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "resize"
version = "0.8.0"
description = "Simple image resampling library in pure Rust."
authors = ["Kornel <[email protected]>", "Kagami Hiiragi <[email protected]>"]
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"
Expand All @@ -12,16 +12,20 @@ license = "MIT"
readme = "README.md"
include = ["Cargo.toml", "README.md", "LICENSE", "src/*.rs"]
edition = "2021"
rust-version = "1.57"
rust-version = "1.60"

[features]
default = ["rayon"]
default = ["std", "rayon"]
std = []
no_std = ["dep:libm", "dep: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]
Expand Down
200 changes: 117 additions & 83 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::sync::Arc;
use std::f32;
use std::fmt;
use std::num::NonZeroUsize;

#[cfg(feature = "rayon")]
use rayon::prelude::*;

/// See [Error]
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub type Result<T, E = Error> = core::result::Result<T, E>;

/// 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.
Expand Down Expand Up @@ -151,8 +171,8 @@ fn lanczos(taps: f32, x: f32) -> f32 {
#[allow(non_snake_case)]
#[allow(non_upper_case_globals)]
pub mod Pixel {
use std::marker::PhantomData;
use crate::formats;
use core::marker::PhantomData;

/// Grayscale, 8-bit.
#[cfg_attr(docsrs, doc(alias = "Grey"))]
Expand Down Expand Up @@ -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<InputSubpixel, OutputSubpixel>(pub(crate) PhantomData<(InputSubpixel, OutputSubpixel)>);
Expand Down Expand Up @@ -387,7 +407,7 @@ impl<Format: PixelFormat> Resizer<Format> {

#[cfg(feature = "rayon")]
fn resample_both_axes(&mut self, src: &[Format::InputPixel], 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;
Expand All @@ -412,7 +432,7 @@ impl<Format: PixelFormat> Resizer<Format> {
// For each pixel in the row, calculate the horizontal resampling and store the result.
self.scale.coeffs_w.par_iter().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()];
Expand All @@ -436,7 +456,7 @@ impl<Format: PixelFormat> Resizer<Format> {
// 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) };
Expand All @@ -450,7 +470,7 @@ impl<Format: PixelFormat> Resizer<Format> {
}

// 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);
Expand Down Expand Up @@ -519,11 +539,20 @@ pub enum Error {
InvalidParameters,
}

#[cfg(feature = "std")]
impl std::error::Error for Error {}

impl From<std::collections::TryReserveError> for Error {
impl From<TryReserveError> for Error {
#[inline(always)]
fn from(_: TryReserveError) -> Self {
Self::OutOfMemory
}
}

#[cfg(not(feature = "std"))]
impl From<hashbrown::TryReserveError> for Error {
#[inline(always)]
fn from(_: std::collections::TryReserveError) -> Self {
fn from(_: hashbrown::TryReserveError) -> Self {
Self::OutOfMemory
}
}
Expand All @@ -538,80 +567,85 @@ impl fmt::Display for Error {
}
}

#[test]
fn oom() {
let _ = new(2, 2, isize::max_value() as _, isize::max_value() as _, Pixel::Gray16, Type::Triangle);
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;

#[test]
fn niche() {
assert_eq!(std::mem::size_of::<Resizer<formats::Gray<f32, f32>>>(), std::mem::size_of::<Option<Resizer<formats::Gray<f32, f32>>>>());
}
#[test]
fn oom() {
let _ = new(2, 2, isize::max_value() as _, isize::max_value() as _, Pixel::Gray16, Type::Triangle);
}

#[test]
fn zeros() {
assert!(new(1, 1, 1, 0, Pixel::Gray16, Type::Triangle).is_err());
assert!(new(1, 1, 0, 1, Pixel::Gray8, Type::Catrom).is_err());
assert!(new(1, 0, 1, 1, Pixel::RGBAF32, Type::Lanczos3).is_err());
assert!(new(0, 1, 1, 1, Pixel::RGB8, Type::Mitchell).is_err());
}
#[test]
fn niche() {
assert_eq!(std::mem::size_of::<Resizer<formats::Gray<f32, f32>>>(), std::mem::size_of::<Option<Resizer<formats::Gray<f32, f32>>>>());
}

#[test]
fn premultiply() {
use px::RGBA;
let mut r = new(2, 2, 3, 4, Pixel::RGBA8P, Type::Triangle).unwrap();
let mut dst = vec![RGBA::new(0u8,0,0,0u8); 12];
r.resize(&[
RGBA::new(255,127,3,255), RGBA::new(0,0,0,0),
RGBA::new(255,255,255,0), RGBA::new(0,255,255,0),
], &mut dst).unwrap();
assert_eq!(&dst, &[
RGBA { r: 255, g: 127, b: 3, a: 255 }, RGBA { r: 255, g: 127, b: 3, a: 128 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 255, g: 127, b: 3, a: 191 }, RGBA { r: 255, g: 127, b: 3, a: 96 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 255, g: 127, b: 3, a: 64 }, RGBA { r: 255, g: 127, b: 3, a: 32 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 0, g: 0, b: 0, a: 0 }, RGBA { r: 0, g: 0, b: 0, a: 0 }, RGBA { r: 0, g: 0, b: 0, a: 0 }
]);
}
#[test]
fn zeros() {
assert!(new(1, 1, 1, 0, Pixel::Gray16, Type::Triangle).is_err());
assert!(new(1, 1, 0, 1, Pixel::Gray8, Type::Catrom).is_err());
assert!(new(1, 0, 1, 1, Pixel::RGBAF32, Type::Lanczos3).is_err());
assert!(new(0, 1, 1, 1, Pixel::RGB8, Type::Mitchell).is_err());
}

#[test]
fn premultiply_solid() {
use px::RGBA;
let mut r = new(2, 2, 3, 4, Pixel::RGBA8P, Type::Triangle).unwrap();
let mut dst = vec![RGBA::new(0u8,0,0,0u8); 12];
r.resize(&[
RGBA::new(255,255,255,255), RGBA::new(0,0,0,255),
RGBA::new(0,0,0,255), RGBA::new(0,0,0,255),
], &mut dst).unwrap();
assert_eq!(&dst, &[
RGBA { r: 255, g: 255, b: 255, a: 255 }, RGBA { r: 128, g: 128, b: 128, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 191, g: 191, b: 191, a: 255 }, RGBA { r: 96, g: 96, b: 96, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 64, g: 64, b: 64, a: 255 }, RGBA { r: 32, g: 32, b: 32, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 0, g: 0, b: 0, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
]);
}
#[test]
fn premultiply() {
use px::RGBA;
let mut r = new(2, 2, 3, 4, Pixel::RGBA8P, Type::Triangle).unwrap();
let mut dst = vec![RGBA::new(0u8,0,0,0u8); 12];
r.resize(&[
RGBA::new(255,127,3,255), RGBA::new(0,0,0,0),
RGBA::new(255,255,255,0), RGBA::new(0,255,255,0),
], &mut dst).unwrap();
assert_eq!(&dst, &[
RGBA { r: 255, g: 127, b: 3, a: 255 }, RGBA { r: 255, g: 127, b: 3, a: 128 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 255, g: 127, b: 3, a: 191 }, RGBA { r: 255, g: 127, b: 3, a: 96 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 255, g: 127, b: 3, a: 64 }, RGBA { r: 255, g: 127, b: 3, a: 32 }, RGBA { r: 0, g: 0, b: 0, a: 0 },
RGBA { r: 0, g: 0, b: 0, a: 0 }, RGBA { r: 0, g: 0, b: 0, a: 0 }, RGBA { r: 0, g: 0, b: 0, a: 0 }
]);
}

#[test]
fn resize_stride() {
use rgb::FromSlice;

let mut r = new(2, 2, 3, 4, Pixel::Gray16, Type::Triangle).unwrap();
let mut dst = vec![0; 12];
r.resize_stride(&[
65535,65535,1,2,
65535,65535,3,4,
].as_gray(), 4, dst.as_gray_mut()).unwrap();
assert_eq!(&dst, &[65535; 12]);
}
#[test]
fn premultiply_solid() {
use px::RGBA;
let mut r = new(2, 2, 3, 4, Pixel::RGBA8P, Type::Triangle).unwrap();
let mut dst = vec![RGBA::new(0u8,0,0,0u8); 12];
r.resize(&[
RGBA::new(255,255,255,255), RGBA::new(0,0,0,255),
RGBA::new(0,0,0,255), RGBA::new(0,0,0,255),
], &mut dst).unwrap();
assert_eq!(&dst, &[
RGBA { r: 255, g: 255, b: 255, a: 255 }, RGBA { r: 128, g: 128, b: 128, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 191, g: 191, b: 191, a: 255 }, RGBA { r: 96, g: 96, b: 96, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 64, g: 64, b: 64, a: 255 }, RGBA { r: 32, g: 32, b: 32, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
RGBA { r: 0, g: 0, b: 0, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 }, RGBA { r: 0, g: 0, b: 0, a: 255 },
]);
}

#[test]
fn resize_float() {
use rgb::FromSlice;

let mut r = new(2, 2, 3, 4, Pixel::GrayF32, Type::Triangle).unwrap();
let mut dst = vec![0.; 12];
r.resize_stride(&[
65535.,65535.,1.,2.,
65535.,65535.,3.,4.,
].as_gray(), 4, dst.as_gray_mut()).unwrap();
assert_eq!(&dst, &[65535.; 12]);
#[test]
fn resize_stride() {
use rgb::FromSlice;

let mut r = new(2, 2, 3, 4, Pixel::Gray16, Type::Triangle).unwrap();
let mut dst = vec![0; 12];
r.resize_stride(&[
65535,65535,1,2,
65535,65535,3,4,
].as_gray(), 4, dst.as_gray_mut()).unwrap();
assert_eq!(&dst, &[65535; 12]);
}

#[test]
fn resize_float() {
use rgb::FromSlice;

let mut r = new(2, 2, 3, 4, Pixel::GrayF32, Type::Triangle).unwrap();
let mut dst = vec![0.; 12];
r.resize_stride(&[
65535.,65535.,1.,2.,
65535.,65535.,3.,4.,
].as_gray(), 4, dst.as_gray_mut()).unwrap();
assert_eq!(&dst, &[65535.; 12]);
}
}
Loading

0 comments on commit 46b63be

Please sign in to comment.