Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basic no_std support #1868

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
24 changes: 19 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ name: Rust CI

on:
push:
branches: [ master, next ]
pull_request:
branches: [ master, next ]
# TODO(interstellar) revert
# branches: [ master, next ]
# pull_request:
# branches: [ master, next ]

jobs:
build:
Expand All @@ -13,7 +14,7 @@ jobs:
strategy:
matrix:
rust: ["1.61.0", stable, beta, nightly]
features: [gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dxt, dds, farbfeld, openexr, jpeg_rayon, webp-encoder, '']
features: [gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dxt, dds, farbfeld, openexr, jpeg_rayon, webp-encoder, std]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
Expand Down Expand Up @@ -45,7 +46,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
features: [gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dxt, dds, farbfeld, openexr, jpeg_rayon, webp-encoder, '']
features: [gif, jpeg, png, tiff, ico, pnm, tga, webp, bmp, hdr, dxt, dds, farbfeld, openexr, jpeg_rayon, webp-encoder, std]

# we are using the cross project for cross compilation to mips:
# https://github.com/cross-rs/cross
Expand Down Expand Up @@ -120,6 +121,19 @@ jobs:
env:
SYSTEM_DEPS_DAV1D_BUILD_INTERNAL: always

no_std_check:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: build
run: |
cargo install cargo-no-std-check
cargo no-std-check --no-default-features --features=alloc

clippy:
runs-on: ubuntu-20.04
steps:
Expand Down
91 changes: 69 additions & 22 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ path = "./src/lib.rs"

[dependencies]
bytemuck = { version = "1.7.0", features = ["extern_crate_alloc"] } # includes cast_vec
byteorder = "1.3.2"
byteorder = { version = "1.3.2", default-features = false }
num-rational = { version = "0.4", default-features = false }
num-traits = "0.2.0"
gif = { version = "0.12", optional = true }
num-traits = { version = "0.2", default-features = false }
gif = { version = "0.12", optional = true, default-features = false }
jpeg = { package = "jpeg-decoder", version = "0.3.0", default-features = false, optional = true }
png = { version = "0.17.6", optional = true }
scoped_threadpool = { version = "0.1", optional = true }
Expand All @@ -44,7 +44,9 @@ rgb = { version = "0.8.25", optional = true }
mp4parse = { version = "0.12.0", optional = true }
dav1d = { version = "0.6.0", optional = true }
dcv-color-primitives = { version = "0.4.0", optional = true }
color_quant = "1.1"
# TODO(interstellar) no_std not supported on crate.io; need a fork/patch
# NOTE: also an optional dependency of "gif"
color_quant = { version = "1.1", git = "https://github.com/Interstellar-Network/color_quant.git", branch = "sgx-nostd-compat", default-features = false, optional = true }
exr = { version = "1.5.0", optional = true }
qoi = { version = "0.4", optional = true }
libwebp = { package = "webp", version = "0.2.2", default-features = false, optional = true }
Expand All @@ -61,38 +63,47 @@ jpeg = { package = "jpeg-decoder", version = "0.3.0", default-features = false,

[features]
# TODO: Add "avif" to this list while preparing for 0.24.0
default = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld", "jpeg_rayon", "openexr", "qoi"]

ico = ["bmp", "png"]
pnm = []
tga = []
bmp = []
hdr = ["scoped_threadpool"]
dxt = []
default = ["std", "gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld", "jpeg_rayon", "openexr", "qoi"]

# MUST always compile with either "std" or "alloc"
# TODO(interstellar) add compile error in case of neither?
std = ["num-traits/std", "byteorder/std", "color_quant/std"]
alloc = ["num-traits/libm", "color_quant/num-traits"]

gif = ["std", "gif/default"]
jpeg = ["std", "dep:jpeg"]
png = ["std", "dep:png"]
tiff = ["std", "dep:tiff"]
ico = ["std", "bmp", "png"]
pnm = ["std"]
tga = ["std"]
bmp = ["std"]
hdr = ["std", "scoped_threadpool"]
dxt = ["std"]
dds = ["dxt"]
farbfeld = []
openexr = ["exr"]
qoi = ["dep:qoi"]
farbfeld = ["std"]
openexr = ["std", "exr"]
qoi = ["std", "dep:qoi"]

# Enables WebP decoder support.
webp = []
webp = ["std"]
# Non-default, not included in `webp`. Requires native dependency libwebp.
webp-encoder = ["libwebp"]
webp-encoder = ["std", "libwebp"]

# Enables multi-threading.
# Requires latest stable Rust.
jpeg_rayon = ["jpeg/rayon"]
jpeg_rayon = ["std", "jpeg/rayon"]
# Non-default, enables avif support.
# Requires latest stable Rust.
avif = ["avif-encoder"]
avif = ["std", "avif-encoder"]
# Requires latest stable Rust and recent nasm (>= 2.14).
avif-encoder = ["ravif", "rgb"]
avif-encoder = ["std", "ravif", "rgb"]
# Non-default, even in `avif`. Requires stable Rust and native dependency libdav1d.
avif-decoder = ["mp4parse", "dcv-color-primitives", "dav1d"]
avif-decoder = ["std", "mp4parse", "dcv-color-primitives", "dav1d"]

# Build some inline benchmarks. Useful only during development.
# Requires rustc nightly for feature test.
benchmarks = []
benchmarks = ["std"]

[[bench]]
path = "benches/decode.rs"
Expand All @@ -107,3 +118,39 @@ harness = false
[[bench]]
name = "copy_from"
harness = false

[[example]]
name = "tile"
required-features = ["std"]

[[example]]
name = "fractal"
required-features = ["std"]

[[example]]
name = "scaleup"
required-features = ["std"]

[[example]]
name = "concat"
required-features = ["std"]

[[example]]
name = "gradient"
required-features = ["std"]

[[example]]
name = "opening"
required-features = ["std"]

[[example]]
name = "decode"
required-features = ["std"]

[[example]]
name = "scaledown"
required-features = ["std"]

[[example]]
name = "convert"
required-features = ["std"]
37 changes: 21 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ All image processing functions provided operate on types that implement the `Gen

`image` provides implementations of common image format encoders and decoders.

| Format | Decoding | Encoding |
| ------ | -------- | -------- |
| PNG | All supported color types | Same as decoding |
| JPEG | Baseline and progressive | Baseline JPEG |
| GIF | Yes | Yes |
| BMP | Yes | Rgb8, Rgba8, Gray8, GrayA8 |
| ICO | Yes | Yes |
| TIFF | Baseline(no fax support) + LZW + PackBits | Rgb8, Rgba8, Gray8 |
| WebP | Yes | Rgb8, Rgba8 \* |
| AVIF | Only 8-bit \*\* | Lossy |
| PNM | PBM, PGM, PPM, standard PAM | Yes |
| DDS | DXT1, DXT3, DXT5 | No |
| TGA | Yes | Rgb8, Rgba8, Bgr8, Bgra8, Gray8, GrayA8 |
| OpenEXR | Rgb32F, Rgba32F (no dwa compression) | Rgb32F, Rgba32F (no dwa compression) |
| farbfeld | Yes | Yes |
| Format | Decoding | Encoding |
| -------- | ----------------------------------------- | --------------------------------------- |
| PNG | All supported color types | Same as decoding |
| JPEG | Baseline and progressive | Baseline JPEG |
| GIF | Yes | Yes |
| BMP | Yes | Rgb8, Rgba8, Gray8, GrayA8 |
| ICO | Yes | Yes |
| TIFF | Baseline(no fax support) + LZW + PackBits | Rgb8, Rgba8, Gray8 |
| WebP | Yes | Rgb8, Rgba8 \* |
| AVIF | Only 8-bit \*\* | Lossy |
| PNM | PBM, PGM, PPM, standard PAM | Yes |
| DDS | DXT1, DXT3, DXT5 | No |
| TGA | Yes | Rgb8, Rgba8, Bgr8, Bgra8, Gray8, GrayA8 |
| OpenEXR | Rgb32F, Rgba32F (no dwa compression) | Rgb32F, Rgba32F (no dwa compression) |
| farbfeld | Yes | Yes |

- \* Requires the `webp-encoder` feature, uses the libwebp C library.
- \*\* Requires the `avif-decoder` feature, uses the libdav1d C library.
Expand Down Expand Up @@ -60,7 +60,7 @@ The most important methods for decoders are...
All pixels are parameterised by their component type.

## Images
Individual pixels within images are indexed with (0,0) at the top left corner.
Individual pixels within images are indexed with (0,0) at the top left corner.
### The [`GenericImageView`](https://docs.rs/image/*/image/trait.GenericImageView.html) and [`GenericImage`](https://docs.rs/image/*/image/trait.GenericImage.html) Traits

Traits that provide methods for inspecting (`GenericImageView`) and manipulating (`GenericImage`) images, parameterised over the image's pixel type.
Expand Down Expand Up @@ -166,6 +166,7 @@ reader which offer some more control.
```rust,no_run
use image::GenericImageView;

#[cfg(feature = "std")]
fn main() {
// Use the open function to load an image from a Path.
// `open` returns a `DynamicImage` on success.
Expand All @@ -180,6 +181,8 @@ fn main() {
// Write the contents of this image to the Writer in PNG format.
img.save("test.png").unwrap();
}
#[cfg(not(feature = "std"))]
fn main() {}
```

### Generating Fractals
Expand Down Expand Up @@ -225,6 +228,7 @@ fn main() {
}

// Save the image as “fractal.png”, the format is deduced from the path
#[cfg(feature = "std")]
imgbuf.save("fractal.png").unwrap();
}
```
Expand All @@ -242,6 +246,7 @@ fn main() {
let buffer: &[u8] = unimplemented!(); // Generate the image data

// Save the buffer as "image.png"
#[cfg(feature = "std")]
image::save_buffer("image.png", buffer, 800, 600, image::ColorType::Rgb8).unwrap()
}
```
10 changes: 7 additions & 3 deletions src/animation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use std::iter::Iterator;
use std::time::Duration;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::cmp::Ordering::Equal;
use core::cmp::Ordering::Less;
use core::iter::Iterator;
use core::time::Duration;

use num_rational::Ratio;

Expand Down Expand Up @@ -179,7 +183,7 @@ impl Delay {
/// Note that `denom_bound` bounds nominator and denominator of all intermediate
/// approximations and the end result.
fn closest_bounded_fraction(denom_bound: u32, nom: u32, denom: u32) -> (u32, u32) {
use std::cmp::Ordering::{self, *};
use core::cmp::Ordering::{self, *};
assert!(0 < denom);
assert!(0 < denom_bound);
assert!(nom < denom);
Expand Down
25 changes: 18 additions & 7 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
//! Contains the generic `ImageBuffer` struct.
use alloc::vec::Vec;
use core::fmt;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut, Index, IndexMut, Range};
use core::slice::{ChunksExact, ChunksExactMut};
use num_traits::Zero;
use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut, Index, IndexMut, Range};
use std::path::Path;
use std::slice::{ChunksExact, ChunksExactMut};

use crate::color::{FromColor, Luma, LumaA, Rgb, Rgba};
#[cfg(feature = "std")]
use crate::dynimage::{save_buffer, save_buffer_with_format, write_buffer_with_format};
use crate::error::ImageResult;
use crate::flat::{FlatSamples, SampleLayout};
Expand Down Expand Up @@ -629,6 +630,7 @@ where
/// Overlays an image on top of a larger background raster.
///
/// ```no_run
/// # #[cfg(feature = "std")] {
/// use image::{GenericImage, GenericImageView, ImageBuffer, open};
///
/// let on_top = open("path/to/some.png").unwrap().into_rgb8();
Expand All @@ -641,15 +643,18 @@ where
/// });
///
/// image::imageops::overlay(&mut img, &on_top, 128, 128);
/// }
/// ```
///
/// Convert an RgbaImage to a GrayImage.
///
/// ```no_run
/// # #[cfg(feature = "std")] {
/// use image::{open, DynamicImage};
///
/// let rgba = open("path/to/some.png").unwrap().into_rgba8();
/// let gray = DynamicImage::ImageRgba8(rgba).into_luma8();
/// }
/// ```
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct ImageBuffer<P: Pixel, Container> {
Expand Down Expand Up @@ -979,6 +984,7 @@ where
}
}

#[cfg(feature = "std")]
impl<P, Container> ImageBuffer<P, Container>
where
P: Pixel,
Expand All @@ -990,7 +996,7 @@ where
/// The image format is derived from the file extension.
pub fn save<Q>(&self, path: Q) -> ImageResult<()>
where
Q: AsRef<Path>,
Q: AsRef<std::path::Path>,
P: PixelWithColorType,
{
save_buffer(
Expand All @@ -1003,6 +1009,7 @@ where
}
}

#[cfg(feature = "std")]
impl<P, Container> ImageBuffer<P, Container>
where
P: Pixel,
Expand All @@ -1016,7 +1023,7 @@ where
/// supported types.
pub fn save_with_format<Q>(&self, path: Q, format: ImageFormat) -> ImageResult<()>
where
Q: AsRef<Path>,
Q: AsRef<std::path::Path>,
P: PixelWithColorType,
{
// This is valid as the subpixel is u8.
Expand All @@ -1031,6 +1038,7 @@ where
}
}

#[cfg(feature = "std")]
impl<P, Container> ImageBuffer<P, Container>
where
P: Pixel,
Expand Down Expand Up @@ -1360,11 +1368,13 @@ where
/// use image::GrayImage;
///
/// let image_path = "examples/fractal.png";
/// # #[cfg(feature = "std")] {
/// let image = image::open(&image_path)
/// .expect("Open file failed")
/// .to_rgba8();
///
/// let gray_image: GrayImage = image.convert();
/// }
/// ```
fn convert(&self) -> ImageBuffer<ToType, Vec<ToType::Subpixel>> {
let mut buffer: ImageBuffer<ToType, Vec<ToType::Subpixel>> =
Expand Down Expand Up @@ -1407,6 +1417,7 @@ mod test {
use crate::math::Rect;
use crate::GenericImage as _;
use crate::{color, Rgb};
use alloc::vec::Vec;

#[test]
/// Tests if image buffers from slices work
Expand Down