-
Notifications
You must be signed in to change notification settings - Fork 89
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
Type-safe handling of various pixel types #223
Comments
The With some insight on the state of Rust graphics programming—including the sentiment derived from Firstly, the above requires agreeing on one particular exact SemVer type for RGB. I'm sorry, but no. Many callers care about the data foremost and turning enum variants into runtime representation (of 4CC flavor) is super akward compared to just getting one in the first place. Secondly, it requires agreeing on one particular style of allocation. The container In short: it's a vain effort to try to fulfill all the different requirements users may have on the allocation and container type of their data with standard containers; and the more so by a finite list of enum variants. So we don't attempt to. Instead, rely on the fact that all channel types are simple and bytes compatible. This may require one re-allocation at the moment but the better fix for this would be to take a user-allocated buffer ( |
It's safe and zero-copy to cast Current state in jpeg-decoder is pretty bad to the point I'm panicking if I get |
That definitely does not sound right. Can you expand? Is this a panic within |
I mean I've chosen not to handle L16 the hard way with pointer arithmetic, and if the vec doesn't happen to be aligned, I call panic. |
Not zero-cost but one Are you content with the |
I actually do need to copy the buffer myself to yet another representation (in this particular case I wanted 8-bit RGB or RGBA regardless of input format), so lack of ability to get a slice means I need to copy twice. I get that you're trying to return the lowest common denominator, but due to endian and alignment, Vec of u8 isn't it. The need to use bytemuck and copy isn't as much of a perf issue as annoyance that I need extra code and extra inefficiency only for something that I consider an inelegant and dated API design. To use it without It's a C-style type-unsafe pattern. If I keep the pixels in this format, it's error prone on every use (I have to keep the two fields in sync). OTOH Enum variants for pixel layouts force checking where it's relevant, and allow branching into statically-typed code paths and generics. I know lots of 90's-style APIs use a A match on an If you don't want to depend on my Or even if you don't believe that pixels should be |
The more I've dealt with the different ways of dealing with encoding image data with Rust types, the more convinced I am that there is no elegant options, only varying degrees of bad options. The "type safe" design you describe in your original post is the one we used in the image-tiff crate, and it can cause tons of boilerplate. In this case, the option I'd probably prefer would be having decode write its output into a decoder.read_info()?;
let info = decoder.info().unwrap();
assert_eq!(info.pixel_format, PixelFormat::L16);
let mut data = vec![0u16; info.width as usize * info.height as usize];
decoder.decode(bytemuck::cast_slice_mut(data))?; |
The lossless decoder that produces L16 creates an |
The boilerplate you've linked to is not bad. It can be moved to a method, even |
Sorry, my comment wasn't clear. Each of the linked words is different part of that file. In total there's probably 400+ lines of boilerplate code in that one file alone. |
The first link that sets up the types and constructors is a bit boilerplatey, but it could be made cleaner with a couple of declarative macros. The remaining three links are actually useful work. If you don't do it in your code, you're not avoiding this boilerplate, you're pushing it down on users. This is exactly the problem I came here to complain about. Now I have to either do copies in endian-aware way myself, or write even nastier unsafe code for unaligned reads. That's not saving boilerplate. It's cutting out code by providing less useful API and requiring more busywork for your users. I don't understand how am I supposed to process 16-bit image data without ever converting the pixel data to |
I'm using this crate again, and again I'm running into the undocumented endian + no safe zero-copy use of L16. |
The pair of
pixel_format
andpixels
types is very C-like, without type safety.This is especially clear with the addition of
L16
format, which is difficult to safely read. Reading it as.chunks(2)
requires dealing with endianness (the docs don't say which endian it is), and casting it to&[u16]
is not possible to do safely due to risk of insufficient alignment. So assuming it's a native endian, it requires a totallyunsafe
loop with pointer arithmetic andptr::read_unaligned
.It would be better to expose pixel formats in a type-safe manner, like:
The text was updated successfully, but these errors were encountered: