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

no_std support #1184

Open
vadixidav opened this issue Apr 3, 2020 · 14 comments
Open

no_std support #1184

vadixidav opened this issue Apr 3, 2020 · 14 comments

Comments

@vadixidav
Copy link

I would like image to support no_std.

I am using it in the akaze crate. To be able to support embedded robotics/drone targets, it needs to support no_std. However, image is used for the input types to integrate into the ecosystem more cleanly.

This is important for utilizing image across the whole ecosystem, including in embedded targets, where they may have images coming in from attached cameras or even just when reading/writing images being sent over a bus to a screen.

The most basic way to implement this functionality is just to create a std feature that is included by default to allow backwards compatibility. This feature would gate all functions that are specific to an OS. This includes image::open, which can only execute with a file system.

I would also recommend adding a GitHub action to test that no_std support actually works in CI. A good example of this is in some crates I have recently worked on such as eight-point.

@HeroicKatora
Copy link
Member

HeroicKatora commented Apr 3, 2020

Depending alloc in a #[no_std] crate was stabilized only in 1.36. It's not completely unlikely that we'll run into some MSRV problem. That could defer completion until the next major release, there's also an appropriate tag to set if any issues are encountered. (I'm assuming you're not asking for no-alloc, that would be way off and better solved by a specialized crate).

@fintelia
Copy link
Contributor

fintelia commented Apr 3, 2020

This issue is even harder than it might initially seem because the Read and Write standard library traits our encoders/decoders depend on don't work with no_std.

@vadixidav
Copy link
Author

vadixidav commented Apr 8, 2020

Is it possible to feature gate any code that requires Read and Write behind the std feature? I only need image for its image types, for instance. I am using them (with liballoc) to act as a standard way of passing the image data around and doing conversions/basic rescale operations. Once Read and Write are supported in no_std, we can just add those impls/fns later.

@HeroicKatora
Copy link
Member

It's not just the methods but even the ubiquitous Error type uses std::io::Error and thus is on its own currently incompatible with no_std. This then 'bleeds' into many other interfaces that use that central type directly, in particular even into GenericImage. So this would leave a lot less remaining than it would remove.

This is however useful to plan for 0.24, the earliest where we could do the required interface changes. First, we'd need to identify methods that should use more concrete error types. These could then change in the next release and bring us closer to a useful #[no_std] portion.

@luojia65
Copy link

Any further advancements? :) We may provide operation system only APIs under cfg(not(no_std)), and provide byte slice based APIs for embedded devices.
(Could be done before a probable 1.0 release)

@HeroicKatora
Copy link
Member

HeroicKatora commented Jun 20, 2020

Supporting byte slices in a separate interface is definitely a possible avenue for no_std/no-alloc. I had hoped to find a good a replacement for io::{Read,Write} (and indeed there exists core_io) but the migration path is rather painful. That is, it offers no SemVer compatible migration and feature switch because it's handled by Rust like a completely separate user crate.

@luojia65
Copy link

luojia65 commented Jun 20, 2020

I don't know exact, but embedded-graphics::image has provided with a possible approach. And there are simple file system drivers like embedded-sdmmc where further abstractions could be made. We may not use core_io, but define a special trait for image crate, which may rely on other possible API designs for no_std environments. (I'd look into both projects further)

@HeroicKatora
Copy link
Member

HeroicKatora commented Jun 20, 2020

We may not use core_io, but define a special trait for image crate, which may rely on other possible API designs for no_std environments.

Yep, the important part would be that there needs to be a way to support both. In particular, many of our decoder structures look like so:

struct Decoder<R> {
    reader: R,
    // .. other fields
}

impl<R: io::Read> Decoder<R> {
    fn decode(&mut self, into: &[u8]) -> io::Read<R> {}
}

Now, we can modify the inner parts of the struct (e.g. turn reader: R into reader: SomeEnum<R>) but we must keep the same trait bound from working. Since for example &[u8]: std::io::Read this is difficult to do in a coherent way. Imagine a crate defines a replacement trait CoreRead and implements it for &[u8]. Now we can't selectively change the trait bounds of decode based on a feature flag, that would be an incompatible design. On the other hand, there can also not be a blanket impl such as:

impl<T: std::io::Read> CoreRead for T

because it would collide with the other impl for byte slices.

@HeroicKatora
Copy link
Member

As an additional remark, we could make a major version bump for this feature. However, we shouldn't make more than one imo. That is, any design that requires a major version bump should at least consider and if possible explicitely state:

  • how it's concrete interface looks like
  • how it improves over the possible designs that do not require such a major version bump
  • why it is unlikely that another bump is required (e.g. show usage or how it replaces multiple existing uses of specialized no_std decoders)
  • how it is applicable to more than one of the images formats, and evaluate how much it locks us into particular implementations.

@vadixidav
Copy link
Author

Is this something that has been investigated at this point? To explain the problem concretely, in rust-cv we need to occasionally pass images around, and so far we have been trying to do that with the image crate. We are thinking about creating a separate library with no_std support in mind for handling images due to the fact that the image crate doesn't support chroma subsampling, which was a problem for the eye crate in particular (for capturing raw image data from the camera, and utilizing just the native luminosity data). However, it would definitely help if we could provide APIs for embedded that allow people to pass images around using the types in this crate. We aren't interested in any I/O, just image manipulation, such as with imageproc. We can provide the raw image data to the image crate from another crate like ndarray or similar. The intention is to use image as a way to store, pass around, and process image types, rather than as some means to load or save images.

I don't have any recommendations on how to achieve this, just that it is of interest.

@HeroicKatora
Copy link
Member

Is this something that has been investigated at this point?

The non-io portion of passing images in memory is indeed being investigated in image-canvas. Please have a look, and mind that 0.0.6 is nice but some exciting new ideas are only available on its master branch.

@n-prat
Copy link

n-prat commented Feb 24, 2023

Hello,

I'm revisiting this b/c I needed to use this crate(or rather imageproc but I'm starting with this one) in a SGX env, which means I had to make it work in a no_std-like env.

So I am now trying to rewrite/cleanup my fork for proper no_std support.
I believe I have a starting point, cf tests results below.

tests results `cargo test --no-default-features`
running 86 tests
test animation::tests::fps_30 ... ok
test animation::tests::duration_approx ... ok
test animation::tests::duration_outlier ... ok
test animation::tests::simple ... ok
test buffer_::test::default ... ok
test animation::tests::precise ... ok
test buffer_::test::get_pixel ... ok
test buffer_::test::exact_size_iter_size_hint ... ok
test buffer_::test::get_pixel_checked ... ok
test buffer_::test::nonzero_width_zero_height ... ok
test buffer_::test::pixels_on_large_buffer ... ok
test buffer_::test::mut_iter ... ok
test buffer_::test::slice_buffer ... ok
test buffer_::test::test_image_buffer_copy_within_bl ... ok
test buffer_::test::test_image_buffer_copy_within_tl ... ok
test buffer_::test::test_image_buffer_copy_within_br ... ok
test color::tests::accuracy_conversion ... ok
test buffer_::test::test_image_buffer_copy_within_oob ... ok
test color::tests::test_apply_with_alpha_rgb ... ok
test color::tests::test_apply_with_alpha_rgba ... ok
test buffer_::test::zero_width_nonzero_height ... ok
test color::tests::test_apply_without_alpha_rgb ... ok
test buffer_::test::zero_width_zero_height ... ok
test buffer_::test::test_image_buffer_copy_within_tr ... ok
test color::tests::test_blend_luma_alpha ... ok
test color::tests::test_blend_rgba ... ok
test color::tests::test_lossless_conversions ... ok
test color::tests::test_map_with_alpha_rgba ... ok
test color::tests::test_apply_without_alpha_rgba ... ok
test color::tests::test_map_without_alpha_rgb ... ok
test error::tests::test_send_sync_stability ... ok
test flat::tests::normal_forms ... ok
test image::tests::test_copy_sub_image ... ok
test image::tests::test_generic_image_copy_within_bl ... ok
test color::tests::test_map_with_alpha_rgb ... ok
test flat::tests::mutable_view ... ok
test color::tests::test_map_without_alpha_rgba ... ok
test image::tests::test_generic_image_copy_within_tr ... ok
test image::tests::test_generic_image_copy_within_br ... ok
test image::tests::test_can_nest_views ... ok
test image::tests::test_generic_image_copy_within_oob ... ok
test image::tests::test_can_subimage_clone_nonmut ... ok
test flat::tests::image_buffer_conversion ... ok
test image::tests::test_generic_image_copy_within_tl ... ok
test image::tests::test_image_alpha_blending ... ok
test image::tests::test_in_bounds ... ok
test image::tests::test_view_height_out_of_bounds - should panic ... ok
test image::tests::test_view_in_bounds ... ok
test image::tests::test_view_coordinates_out_of_bounds - should panic ... ok
test image::tests::test_view_width_out_of_bounds - should panic ... ok
test image::tests::test_view_x_out_of_bounds - should panic ... ok
test imageops::affine::test::test_flip_horizontal ... ok
test imageops::affine::test::test_flip_horizontal_in_place ... ok
test image::tests::test_view_y_out_of_bounds - should panic ... ok
test image::tests::test_view_out_of_bounds - should panic ... ok
test imageops::affine::test::test_flip_vertical ... ok
test imageops::affine::test::test_flip_vertical_in_place ... ok
test imageops::affine::test::test_rotate180 ... ok
test imageops::affine::test::test_rotate180_in_place ... ok
test imageops::affine::test::test_rotate270 ... ok
test imageops::affine::test::test_rotate90 ... ok
test imageops::colorops::test::test_brighten ... ok
test imageops::colorops::test::test_brighten_place ... ok
test imageops::colorops::test::test_dither ... ok
test animation::tests::small ... ok
test imageops::colorops::test::test_invert ... ok
test imageops::colorops::test::test_grayscale ... ok
test imageops::tests::test_image_horizontal_gradient_limits ... ok
test imageops::tests::test_image_in_image ... ok
test imageops::tests::test_image_coordinate_overflow ... ok
test imageops::tests::test_image_in_image_outside_of_bounds ... ok
test imageops::tests::test_image_vertical_gradient_limits ... ok
test imageops::tests::test_image_outside_image_no_wrap_around ... ok
test imageops::tests::test_overlay_bounds_ext ... ok
test math::utils::test::resize_handles_overflow ... ok
test math::utils::test::resize_rounds ... ok
test math::utils::test::resize_handles_zero ... ok
test math::utils::test::resize_never_rounds_to_zero ... ok
test math::utils::test::resize_handles_fill ... ok
test math::utils::test::resize_bounds_correctly_height ... ok
test math::utils::test::resize_bounds_correctly_width ... ok
test utils::test::gray_to_luma8_skip ... ok
test flat::tests::aliasing_view ... ok
test imageops::tests::test_blur_zero ... ok
test imageops::sample::tests::test_issue_186 ... ok
test imageops::sample::tests::bug_1600 ... ok

test result: ok. 86 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.22s

     Running tests/conversions.rs (target/debug/deps/conversions-c87f7db34eed7646)

running 2 tests
test test_rgbau8_to_rgbau16 ... ok
test test_rgbu8_to_rgbu16 ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Running tests/reference_images.rs (target/debug/deps/reference_images-712a6ca2dac61e54)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/regression.rs (target/debug/deps/regression-b932b2e8206915ae)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/save_jpeg.rs (target/debug/deps/save_jpeg-989b48be48e19198)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/truncate_images.rs (target/debug/deps/truncate_images-48e8440a61be43a6)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests image

running 28 tests
test src/image.rs - image::GenericImage::get_pixel_mut (line 981) ... ignored
test src/buffer.rs - buffer_::ImageBuffer (line 651) - compile ... ok
test src/buffer.rs - buffer_::ImageBuffer (line 632) - compile ... ok
test src/buffer.rs - buffer_::ImageBuffer<FromType,Container>::convert (line 1366) - compile ... ok
test src/flat.rs - flat (line 9) - compile ... ok
test src/imageops/mod.rs - imageops::horizontal_gradient (line 303) - compile ... ok
test src/imageops/mod.rs - imageops::tile (line 241) - compile ... ok
test src/imageops/mod.rs - imageops::vertical_gradient (line 269) - compile ... ok
test src/lib.rs - (line 18) - compile ... ok
test src/lib.rs - (line 35) - compile ... ok
test src/animation.rs - animation::Delay::from_saturating_duration (line 133) ... ok
test src/animation.rs - animation::Delay::from_numer_denom_ms (line 116) ... ok
test src/flat.rs - flat::FlatSamples<Buffer>::get_sample (line 528) ... ok
test src/flat.rs - flat::SampleLayout::row_major_packed (line 133) ... ok
test src/image.rs - image::SubImage<I>::view (line 1212) ... ok
test src/flat.rs - flat::FlatSamples<Buffer>::get_mut_sample (line 562) ... ok
test src/image.rs - image::GenericImageView (line 879) ... ok
test src/flat.rs - flat::SampleLayout::column_major_packed (line 163) ... ok
test src/buffer.rs - buffer_::ImageBuffer (line 617) ... ok
test src/lib.rs - (line 460) - compile ... ok
test src/flat.rs - flat::View<Buffer,P>::try_upgrade (line 1197) ... ok
test src/image.rs - image::ImageFormat::from_mime_type (line 155) ... ok
test src/lib.rs - (line 537) - compile ... ok
test src/flat.rs - flat::FlatSamples<&'buf[Subpixel]>::with_monocolor (line 939) ... ok
test src/lib.rs - (line 484) - compile ... ok
test src/imageops/colorops.rs - imageops::colorops::BiLevel (line 374) ... ok
test src/lib.rs - (line 422) ... ok
test src/lib.rs - (line 377) ... ok

test result: ok. 27 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.39s

Would you be interested in a PR?

Note: it still needs more work: I had to butcher error.rs but I can probably do better.
Note2: I had to be liberal with eg #[cfg(feature = "std")] but I can probably replace most of them by a new eg io feature.

I'm open to feedback!

edit: instead of deactivating all io related code, there are other options: https://docs.rs/binrw/latest/binrw/ and https://github.com/dataphract/acid_io
But I do not know these libs so not sure if going that way is a good idea?

@fintelia
Copy link
Contributor

fintelia commented Mar 1, 2023

One thing to be aware of is that there's been talk about moving std::error::Error, std::io::Error, std::io::Read and a number of other types/traits to core so they'd be accessible to no_std environments. On nightly you can already use core::error::Error. Although, admittedly, those discussions have been stretching on for years so it could likely still be a while longer.

@mspiegel
Copy link

core::error::Error is scheduled to be stabilized in Rust 1.81. One step closer to no_std compatibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants