image
image copied to clipboard
Lossy image loading
I would like to be able to load malformed images, such as truncated files. In particular, once the pixel buffer is allocated, return it even on error so it's possible to recover at least part of the data.
My specific use case for this functionality is to generate thumbnails for a set of files where some of them may be partially invalid (e.g. truncated).
This is more generally applicable to implement more reliable image handling. Instead of failing the whole image load, missing pixels can use a default value. The idea is to handle images similarly to browsers: they are able to display images even if they have some issues (as long as the header is valid).
Draft
The main change would be in the image::decoder_to_vec
function:
/// Reads all of the bytes of a decoder into a Vec<T>. No particular alignment
/// of the output buffer is guaranteed.
///
/// Panics if there isn't enough memory to decode the image.
///
+/// If an error occurs after the pixel buffer was created, it is returned. Note
+/// however that it may be incomplete. Pixels that were not read use their
+/// default "zero" value (the memory is initialized and safe to access).
-pub(crate) fn decoder_to_vec<'a, T>(decoder: impl ImageDecoder<'a>) -> ImageResult<Vec<T>>
+pub(crate) fn decoder_to_vec<'a, T>(decoder: impl ImageDecoder<'a>) -> Result<Vec<T>, (Option<Vec<T>>, ImageError)>
where
T: crate::traits::Primitive + bytemuck::Pod,
{
let total_bytes = usize::try_from(decoder.total_bytes());
if total_bytes.is_err() || total_bytes.unwrap() > isize::max_value() as usize {
- return Err(ImageError::Limits(LimitError::from_kind(
- LimitErrorKind::InsufficientMemory,
- )));
+ return Err((None, ImageError::Limits(LimitError::from_kind(
+ LimitErrorKind::InsufficientMemory,
+ ))));
}
let mut buf = vec![num_traits::Zero::zero(); total_bytes.unwrap() / std::mem::size_of::<T>()];
- decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;
- Ok(buf)
+ match decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice())) {
+ Ok(()) => Ok(buf),
+ Err(e) => Err((Some(buf), e))
+ }
}
On error, return the pixel buffer anyway and let the consumer decide what do.
It may be exposed to consumers by adding a load_lossy
function, as a lenient version of load
where bad pixels use their zero value (instead of failing the whole operation). This is somewhat similar to the relationship between String::from_utf8_lossy
and String::from_utf8
.