Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Rust CI

on:
push:
branches: [ master, next ]
pull_request:
branches: [ master, next ]
# branches: [ master, next ]
# pull_request:
# branches: [ master, next ]

jobs:
build:
Expand Down
52 changes: 47 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ 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 }
snafu = { version = "0.7", 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 +45,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,8 +64,9 @@ 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"]
default = ["std", "gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld", "jpeg_rayon", "openexr", "qoi"]

std = ["snafu/std", "num-traits/std", "byteorder/std", "gif/default", "color_quant/std"]
ico = ["bmp", "png"]
pnm = []
tga = []
Expand Down Expand Up @@ -94,6 +98,8 @@ avif-decoder = ["mp4parse", "dcv-color-primitives", "dav1d"]
# Requires rustc nightly for feature test.
benchmarks = []

alloc = ["num-traits/libm", "color_quant/alloc"]

[[bench]]
path = "benches/decode.rs"
name = "decode"
Expand All @@ -107,3 +113,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
29 changes: 14 additions & 15 deletions src/codecs/png.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,10 @@ impl<R: Read> PngDecoder<R> {
}

fn unsupported_color(ect: ExtendedColorType) -> ImageError {
ImageError::Unsupported(UnsupportedError::from_format_and_kind(
ImageFormat::Png.into(),
UnsupportedErrorKind::Color(ect),
))
ImageError::Unsupported {
format: ImageFormat::Png.into(),
kind: UnsupportedErrorKind::Color(ect),
}
}

impl<'a, R: 'a + Read> ImageDecoder<'a> for PngDecoder<R> {
Expand Down Expand Up @@ -668,21 +668,22 @@ impl ImageError {
fn from_png(err: png::DecodingError) -> ImageError {
use png::DecodingError::*;
match err {
#[cfg(features = "std")]
IoError(err) => ImageError::IoError(err),
// The input image was not a valid PNG.
err @ Format(_) => {
ImageError::Decoding(DecodingError::new(ImageFormat::Png.into(), err))
}
err @ Format(_) => ImageError::Decoding {
format: ImageFormat::Png.into(),
},
// Other is used when:
// - The decoder is polled for more animation frames despite being done (or not being animated
// in the first place).
// - The output buffer does not have the required size.
err @ Parameter(_) => ImageError::Parameter(ParameterError::from_kind(
ParameterErrorKind::Generic(err.to_string()),
)),
LimitsExceeded => {
ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
}
err @ Parameter(_) => ImageError::Parameter {
kind: ParameterErrorKind::Generic(err.to_string()),
},
LimitsExceeded => ImageError::Limits {
kind: LimitErrorKind::InsufficientMemory,
},
}
}
}
Expand Down Expand Up @@ -749,8 +750,6 @@ mod tests {

#[test]
fn underlying_error() {
use std::error::Error;

let mut not_png =
std::fs::read("tests/images/png/bugfixes/debug_triangle_corners_widescreen.png")
.unwrap();
Expand Down
1 change: 1 addition & 0 deletions src/codecs/tga/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ impl<W: Write> ImageEncoder for TgaEncoder<W> {
mod tests {
use super::{EncoderError, TgaEncoder};
use crate::{codecs::tga::TgaDecoder, ColorType, ImageDecoder, ImageError};
use alloc::vec::Vec;
use std::{error::Error, io::Cursor};

fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
Expand Down
Loading