diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index abc9f37..01ad3a6 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: ["1.34.2", stable, beta, nightly] + rust: ["1.36.0", stable, beta, nightly] command: [build, test] steps: - uses: actions/checkout@v2 @@ -18,7 +18,7 @@ jobs: run: > cargo build --verbose - name: test - if: ${{ matrix.rust != '1.34.2' }} + if: ${{ matrix.rust != '1.36.0' }} run: > cargo test --tests --benches # TODO: add criterion benchmarks? diff --git a/src/lib.rs b/src/lib.rs index 136ac50..d372368 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,11 +70,15 @@ that this copyright notice remain intact. //! ``` #![forbid(unsafe_code)] +#![no_std] -mod math; -use crate::math::clamp; +extern crate alloc; + +use alloc::{vec, vec::Vec}; +use core::cmp::{max, min}; -use std::cmp::{max, min}; +mod math; +use crate::math::{abs, clamp_round}; const CHANNELS: usize = 4; @@ -291,7 +295,7 @@ impl NeuQuant { /// for frequently chosen neurons, freq[i] is high and bias[i] is negative /// bias[i] = gamma*((1/self.netsize)-freq[i]) fn contest(&mut self, b: f64, g: f64, r: f64, a: f64) -> i32 { - use std::f64; + use core::f64; let mut bestd = f64::MAX; let mut bestbiasd: f64 = bestd; @@ -302,11 +306,11 @@ impl NeuQuant { let bestbiasd_biased = bestbiasd + self.bias[i]; let mut dist; let n = &self.network[i]; - dist = (n.b - b).abs(); - dist += (n.r - r).abs(); + dist = abs(n.b - b); + dist += abs(n.r - r); if dist < bestd || dist < bestbiasd_biased { - dist += (n.g - g).abs(); - dist += (n.a - a).abs(); + dist += abs(n.g - g); + dist += abs(n.a - a); if dist < bestd { bestd = dist; bestpos = i as i32; @@ -394,10 +398,10 @@ impl NeuQuant { /// initializes the color map fn build_colormap(&mut self) { for i in 0usize..self.netsize { - self.colormap[i].b = clamp(self.network[i].b.round() as i32); - self.colormap[i].g = clamp(self.network[i].g.round() as i32); - self.colormap[i].r = clamp(self.network[i].r.round() as i32); - self.colormap[i].a = clamp(self.network[i].a.round() as i32); + self.colormap[i].b = clamp_round(self.network[i].b); + self.colormap[i].g = clamp_round(self.network[i].g); + self.colormap[i].r = clamp_round(self.network[i].r); + self.colormap[i].a = clamp_round(self.network[i].a); } } @@ -423,7 +427,7 @@ impl NeuQuant { q = self.colormap[smallpos]; // swap p (i) and q (smallpos) entries if i != smallpos { - ::std::mem::swap(&mut p, &mut q); + ::core::mem::swap(&mut p, &mut q); self.colormap[i] = p; self.colormap[smallpos] = q; } @@ -445,7 +449,7 @@ impl NeuQuant { } /// Search for best matching color fn search_netindex(&self, b: u8, g: u8, r: u8, a: u8) -> usize { - let mut best_dist = std::i32::MAX; + let mut best_dist = core::i32::MAX; let first_guess = self.netindex[g as usize]; let mut best_pos = first_guess; let mut i = best_pos; diff --git a/src/math.rs b/src/math.rs index 369c2ad..e5ba753 100644 --- a/src/math.rs +++ b/src/math.rs @@ -1,10 +1,28 @@ +//! These implementations are based on `num-traits`' [`FloatCore`]. +//! They have been adapted to the particular needs of `color_quant` and refined +//! through [feedback]. +//! +//! [`FloatCore`]: https://docs.rs/num-traits/0.2.19/num_traits/float/trait.FloatCore.html +//! [feedback]: https://github.com/image-rs/color_quant/pull/24#discussion_r2083587462 + #[inline] -pub(crate) fn clamp(a: i32) -> i32 { - if a < 0 { +pub(crate) fn abs(a: f64) -> f64 { + if a.is_sign_positive() { + a + } else if a.is_sign_negative() { + -a + } else { + core::f64::NAN + } +} + +#[inline] +pub(crate) fn clamp_round(a: f64) -> i32 { + if a < 0. { 0 - } else if a > 255 { + } else if a > 255. { 255 } else { - a + (a + 0.5) as i32 } }