Skip to content

Commit f22c489

Browse files
committed
no_std support
1 parent 4c216e1 commit f22c489

File tree

18 files changed

+169
-7
lines changed

18 files changed

+169
-7
lines changed

Cargo.toml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,18 @@ edition = "2021"
1515
rust-version = "1.65"
1616

1717
[features]
18-
default = ["threads"]
19-
threads = ["dep:rayon", "dep:thread_local"]
18+
default = ["threads", "std"]
19+
20+
# libimagequant makes good use of multi-threading, so disabling threads has a significant performance peanalty
21+
threads = ["dep:rayon", "dep:thread_local", "std"]
22+
2023
# supports up to 2048 colors for palettes, but NOT FOR REMAPPING
2124
large_palettes = []
2225

26+
# To opt-in you must disable the default features to disable `std` and `threads`, and also enable `no_std`
27+
std = []
28+
no_std = ["dep:hashbrown"]
29+
2330
# this is private and unstable for imagequant-sys only, do not use
2431
_internal_c_ffi = []
2532

@@ -35,6 +42,8 @@ arrayvec = { version = "0.7.4", default-features = false }
3542
rgb = { version = "0.8.47", default-features = false, features = ["bytemuck"] }
3643
rayon = { version = "1.10.0", optional = true }
3744
thread_local = { version = "1.1.8", optional = true }
45+
# Used only in no_std
46+
hashbrown = { version = "0.15.4", optional = true, default-features = false, features = ["alloc"] }
3847

3948
[dev-dependencies]
4049
lodepng = "3.10"

imagequant-sys/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ default = ["imagequant/default"]
2222
# libimagequant makes good use of multi-threading, so disabling threads has a significant performance peanalty
2323
threads = ["imagequant/threads"]
2424

25+
# To opt-in you must disable the default features to disable `std` and `threads`, and also enable `no_std`
26+
std = ["imagequant/std"]
27+
no_std = ["imagequant/no_std"]
28+
29+
# Enable if you get errors like "no global memory allocator found" or "`#[panic_handler]` function required, but not found"
30+
no_std_global_handlers = ["no_std"]
2531

2632
# internal for cargo-c only <http://lib.rs/cargo-c>
2733
capi = []

imagequant-sys/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
fn main() {
2+
if cfg!(all(feature = "std", feature = "no_std")) {
3+
println!("cargo::warning=both std and no_std features are enabled in imagequant-sys");
4+
}
25
println!("cargo:include={}", std::env::var("CARGO_MANIFEST_DIR").unwrap());
36
}

imagequant-sys/src/ffi.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
//! Exports API for C programs and C-FFI-compatible languages. See `libimagequant.h` or <https://pngquant.org/lib/> for C docs.
22
//!
33
//! This crate is not supposed to be used in Rust directly. For Rust, see the parent [imagequant](https://lib.rs/imagequant) crate.
4+
#![cfg_attr(all(not(feature = "std"), feature = "no_std"), no_std)]
5+
46
#![allow(non_camel_case_types)]
57
#![allow(clippy::missing_safety_doc)]
68
#![allow(clippy::wildcard_imports)]
79
#![allow(clippy::items_after_statements)]
810
#![allow(clippy::cast_possible_truncation)]
911
#![allow(clippy::cast_possible_wrap)]
1012

13+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
14+
extern crate alloc as std;
15+
1116
use core::ffi::{c_char, c_int, c_uint, c_void};
1217
use core::mem::{ManuallyDrop, MaybeUninit};
1318
use core::{mem, ptr, slice};
1419
use imagequant::capi::*;
1520
use imagequant::Error::LIQ_OK;
1621
use imagequant::*;
1722
use std::ffi::CString;
23+
use std::boxed::Box;
1824

1925
pub use imagequant::Error as liq_error;
2026

@@ -437,6 +443,7 @@ pub extern "C" fn liq_attr_create_with_allocator(_unused: *mut c_void, free: uns
437443
#[no_mangle]
438444
#[inline(never)]
439445
#[allow(deprecated)]
446+
#[cfg_attr(all(feature = "std", feature = "no_std"), deprecated(note = "Cargo features configuration issue: both std and no_std features are enabled in imagequant-sys\nYou must disable default features to use no_std."))]
440447
pub extern "C" fn liq_attr_create() -> Option<Box<liq_attr>> {
441448
liq_attr_create_with_allocator(ptr::null_mut(), libc::free)
442449
}
@@ -780,3 +787,33 @@ fn c_callback_test_c() {
780787
fn ownership_bitflags() {
781788
assert_eq!(4 + 16, (liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_COPY_PIXELS).bits());
782789
}
790+
791+
#[cfg(all(feature = "no_std_global_handlers", not(feature = "std"), feature = "no_std"))]
792+
#[cfg(not(test))]
793+
mod no_std_global_handlers {
794+
use std::alloc::{GlobalAlloc, Layout};
795+
796+
#[cfg(panic = "unwind")]
797+
compile_error!("no_std imagequant-sys must be compiled with panic=abort\n\nset env var CARGO_PROFILE_DEV_PANIC=abort and CARGO_PROFILE_RELEASE_PANIC=abort\nor modify your Cargo.toml to add `panic=\"abort\"` to `[profile.release]` and `[profile.dev]`");
798+
799+
#[panic_handler]
800+
fn panic(_info: &core::panic::PanicInfo) -> ! {
801+
unsafe { libc::abort(); }
802+
}
803+
804+
#[global_allocator]
805+
static GLOBAL_ALLOCATOR: Mallocator = Mallocator;
806+
807+
#[derive(Default)]
808+
pub struct Mallocator;
809+
810+
unsafe impl GlobalAlloc for Mallocator {
811+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
812+
libc::malloc(layout.size() as _).cast()
813+
}
814+
815+
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
816+
libc::free(ptr.cast());
817+
}
818+
}
819+
}

src/attr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ use crate::quant::{mse_to_quality, quality_to_mse, QuantizationResult};
66
use crate::remap::DitherMapMode;
77
use std::sync::Arc;
88

9+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
10+
use crate::no_std_compat::*;
11+
912
/// Starting point and settings for the quantization process
1013
#[derive(Clone)]
14+
#[cfg_attr(all(feature = "std", feature = "no_std"), deprecated(note = "Cargo features configuration issue: both std and no_std features are enabled in imagequant\nYou must disable default features to use no_std."))]
1115
pub struct Attributes {
1216
pub(crate) max_colors: PalLen,
1317
target_mse: f64,

src/capi.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#![allow(missing_docs)]
44
#![allow(clippy::missing_safety_doc)]
55

6+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
7+
use crate::no_std_compat::*;
8+
69
use crate::pal::Palette;
710
use crate::rows::RowCallback;
811
use crate::seacow::{Pointer, RowBitmapMut, SeaCow};

src/hist.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@ use crate::pal::{f_pixel, gamma_lut, PalIndex, ARGBF, MAX_COLORS, RGBA};
44
use crate::quant::QuantizationResult;
55
use crate::rows::{temp_buf, DynamicRows};
66
use crate::Attributes;
7-
use std::collections::{HashMap, HashSet};
87
use core::{fmt, hash, mem};
98
use core::hash::Hash;
109

10+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
11+
use std::{format, boxed::Box, vec::Vec};
12+
13+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
14+
use hashbrown::{HashMap, HashSet};
15+
16+
#[cfg(not(all(not(feature = "std"), feature = "no_std")))]
17+
use std::collections::{HashMap, HashSet};
18+
19+
1120
/// Number of pixels in a given color for [`Histogram::add_colors()`]
1221
///
1322
/// Used for building a histogram manually. Otherwise see [`Histogram::add_image()`]

src/image.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use crate::{PushInCapacity, LIQ_HIGH_MEMORY_LIMIT};
99
use rgb::prelude::*;
1010
use core::mem::{self, MaybeUninit};
1111

12+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
13+
use crate::no_std_compat::*;
14+
1215
/// Describes image dimensions and pixels for the library
1316
///
1417
/// Create one using [`Attributes::new_image()`].

src/kmeans.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use core::cell::RefCell;
77
use rgb::prelude::*;
88
use rgb::Argb;
99

10+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
11+
use crate::no_std_compat::*;
12+
1013
/// K-Means iteration: new palette color is computed from weighted average of colors that map best to that palette entry.
1114
// avoid false sharing
1215
pub(crate) struct Kmeans {

src/lib.rs

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! Converts RGBA images to 8-bit with alpha channel.
44
//!
55
//! See `examples/` directory for example code.
6+
#![cfg_attr(all(not(feature = "std"), feature = "no_std"), no_std)]
7+
68
#![doc(html_logo_url = "https://pngquant.org/pngquant-logo.png")]
79
#![deny(missing_docs)]
810
#![allow(clippy::bool_to_int_with_if)]
@@ -19,6 +21,12 @@
1921
#![allow(clippy::wildcard_imports)]
2022
#![deny(clippy::semicolon_if_nothing_returned)]
2123

24+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
25+
extern crate alloc as std;
26+
27+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
28+
use std::vec::Vec;
29+
2230
mod attr;
2331
mod blur;
2432
mod error;
@@ -138,8 +146,8 @@ fn poke_it() {
138146
assert_eq!(1, liq.min_posterization());
139147
liq.set_min_posterization(0).unwrap();
140148

141-
use std::sync::atomic::AtomicBool;
142-
use std::sync::atomic::Ordering::SeqCst;
149+
use core::sync::atomic::AtomicBool;
150+
use core::sync::atomic::Ordering::SeqCst;
143151
use std::sync::Arc;
144152

145153
let log_called = Arc::new(AtomicBool::new(false));
@@ -204,8 +212,8 @@ fn thread() {
204212
#[test]
205213
fn r_callback_test() {
206214
use core::mem::MaybeUninit;
207-
use std::sync::atomic::AtomicU16;
208-
use std::sync::atomic::Ordering::SeqCst;
215+
use core::sync::atomic::AtomicU16;
216+
use core::sync::atomic::Ordering::SeqCst;
209217
use std::sync::Arc;
210218

211219
let called = Arc::new(AtomicU16::new(0));
@@ -349,3 +357,56 @@ fn test_fixed_colors() {
349357
assert!(pal[55..].iter().any(|&p| p == RGBA::new(c, c, c, 255)));
350358
}
351359
}
360+
361+
#[cfg(all(not(feature = "std"), feature = "no_std"))]
362+
pub(crate) mod no_std_compat {
363+
pub use std::boxed::Box;
364+
pub use std::vec::Vec;
365+
pub use std::format;
366+
367+
extern "C" {
368+
fn pow(_: f64, _: f64) -> f64;
369+
fn powf(_: f32, _: f32) -> f32;
370+
fn sqrt(_: f64) -> f64;
371+
fn sqrtf(_: f32) -> f32;
372+
}
373+
374+
pub(crate) trait NoMath: Sized {
375+
fn mul_add(self, mul: Self, add: Self) -> Self;
376+
fn powi(self, n: u32) -> Self;
377+
fn powf(self, e: Self) -> Self;
378+
fn sqrt(self) -> Self;
379+
}
380+
381+
impl NoMath for f32 {
382+
fn mul_add(self, mul: Self, add: Self) -> Self {
383+
self * mul + add
384+
}
385+
fn powi(self, n: u32) -> Self {
386+
assert_eq!(n, 2);
387+
self * self
388+
}
389+
fn powf(self, e: Self) -> Self {
390+
unsafe { powf(self, e) }
391+
}
392+
fn sqrt(self) -> Self {
393+
unsafe { sqrtf(self) }
394+
}
395+
}
396+
397+
impl NoMath for f64 {
398+
fn mul_add(self, mul: Self, add: Self) -> Self {
399+
self * mul + add
400+
}
401+
fn powi(self, n: u32) -> Self {
402+
assert_eq!(n, 2);
403+
self * self
404+
}
405+
fn powf(self, e: Self) -> Self {
406+
unsafe { pow(self, e) }
407+
}
408+
fn sqrt(self) -> Self {
409+
unsafe { sqrt(self) }
410+
}
411+
}
412+
}

0 commit comments

Comments
 (0)