Skip to content

Commit cdc0363

Browse files
committed
Provide limited metadata decoding with ImageReader
1 parent 92ae249 commit cdc0363

2 files changed

Lines changed: 91 additions & 25 deletions

File tree

src/io/decoder.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,27 @@ pub trait ImageDecoder {
124124
Ok(())
125125
}
126126

127-
/// Request the decoder to only decode the specified viewbox.
127+
/// Request the decoder to only decode the specified viewbox for the _next_ image.
128+
///
129+
/// The decoder should attempt to restrict the amount of decoder image data to the indicated
130+
/// box. It should return `Ok` if it supports the viewbox directly, or `Err` if it does not
131+
/// support that exact viewbox. In the either case the decoder must indicate the new image
132+
/// dimensions in subsequent calls to [`Self::init`].
133+
///
134+
/// In the error case the value contains another viewbox. The decoder should try to
135+
/// overestimate the requested viewbox as little as possible and return the relative viewbox
136+
/// according to that best effort restriction. In particular, if it does not support any
137+
/// viewboxing it should not change how it decodes its image and return the original request as
138+
/// an error. The default implementation always returns an error this way.
128139
///
129140
/// This should only be implemented by decoders that support it without a significant amount of
130141
/// extra buffering. A good default is to check that only a constant allocation overhead may be
131142
/// spent on it.
132143
///
133-
/// It is up to the [`ImageReader`](crate::ImageReader) to adapt the returned image to an
134-
/// unsupported viewbox pivot.
135-
fn set_viewbox(&mut self, _: crate::math::Rect) -> bool {
136-
false
144+
/// It is up to the caller, the [`ImageReader`](crate::ImageReader), to adapt to an unsupported
145+
/// viewbox.
146+
fn viewbox(&mut self, rect: crate::math::Rect) -> Result<(), crate::math::Rect> {
147+
Err(rect)
137148
}
138149
}
139150

src/io/image_reader_type.rs

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ enum Format {
1818

1919
/// A multi-format image reader.
2020
///
21-
/// Wraps an input reader to facilitate automatic detection of an image's format, appropriate
22-
/// decoding method, and dispatches into the set of supported [`ImageDecoder`] implementations.
21+
/// Wraps an input stream to facilitate automatic detection of an image's format, appropriate
22+
/// decoding method, and turn it into an [`ImageDecoder`] implementation. For convenience, it also
23+
/// allows directly decoding into a [`DynamicImage`].
2324
///
2425
/// ## Usage
2526
///
@@ -74,9 +75,21 @@ pub struct ImageFile<R: Read + Seek> {
7475
limits: Limits,
7576
}
7677

78+
/// An abstracted image reader.
79+
///
80+
/// Wraps an input reader after its format was determined. It provides more detailed decoding
81+
/// methods than [`ImageFile`] and under the hood dispatches into the set of supported
82+
/// [`ImageDecoder`] implementations. For decoder interface that are provided for efficiency it
83+
/// negotiates support with the underlying decoder and then emulates them if necessary.
7784
pub struct ImageReader<'lt> {
7885
/// The reader. Should be buffered.
7986
inner: Box<dyn ImageDecoder + 'lt>,
87+
/// An additional viewbox to apply after decoding.
88+
///
89+
/// This is only used if the inner decoder does not support viewboxes directly.
90+
viewbox: Option<crate::math::Rect>,
91+
/// Remaining limits for allocations by the reader.
92+
limits: Limits,
8093
}
8194

8295
impl<'a, R: 'a + BufRead + Seek> ImageFile<R> {
@@ -225,7 +238,11 @@ impl<'a, R: 'a + BufRead + Seek> ImageFile<R> {
225238
let mut decoder = Self::make_decoder(format, self.inner)?;
226239
decoder.set_limits(self.limits.clone())?;
227240

228-
Ok(ImageReader { inner: decoder })
241+
Ok(ImageReader {
242+
inner: decoder,
243+
viewbox: None,
244+
limits: self.limits,
245+
})
229246
}
230247

231248
/// Make a format guess based on the content, replacing it on success.
@@ -303,22 +320,9 @@ impl<'a, R: 'a + BufRead + Seek> ImageFile<R> {
303320
/// Uses the current format to construct the correct reader for the format.
304321
///
305322
/// If no format was determined, returns an `ImageError::Unsupported`.
306-
pub fn decode(mut self) -> ImageResult<DynamicImage> {
307-
let format = self.require_format()?;
308-
309-
let mut limits = self.limits;
310-
311-
let mut decoder = Self::make_decoder(format, self.inner)?;
312-
decoder.set_limits(limits.clone())?;
313-
let layout = decoder.next_layout()?;
314-
315-
// This is technically redundant but it's also cheap.
316-
limits.check_dimensions(layout.width, layout.height)?;
317-
// Check that we do not allocate a bigger buffer than we are allowed to
318-
// FIXME: should this rather go in `DynamicImage::from_decoder` somehow?
319-
limits.reserve(layout.total_bytes())?;
320-
321-
DynamicImage::decoder_to_image(decoder.as_mut(), layout)
323+
pub fn decode(self) -> ImageResult<DynamicImage> {
324+
let mut reader = self.into_reader()?;
325+
reader.decode()
322326
}
323327

324328
fn require_format(&mut self) -> ImageResult<Format> {
@@ -364,22 +368,73 @@ impl ImageFile<BufReader<File>> {
364368
impl ImageReader<'_> {
365369
/// Decode the next image into a `DynamicImage`.
366370
pub fn decode(&mut self) -> ImageResult<DynamicImage> {
371+
// Try to resolve the viewbox via the decoder, fallback to `crop` if that is not possible.
372+
let residual_vb = self.viewbox.and_then(|vb| self.inner.viewbox(vb).err());
373+
367374
let layout = self.inner.next_layout()?;
368-
DynamicImage::decoder_to_image(self.inner.as_mut(), layout)
375+
// This is technically redundant but it's also cheap.
376+
self.limits.check_dimensions(layout.width, layout.height)?;
377+
// Check that we do not allocate a bigger buffer than we are allowed to
378+
// FIXME: should this rather go in `DynamicImage::from_decoder` somehow?
379+
self.limits.reserve(layout.total_bytes())?;
380+
381+
let mut image = DynamicImage::decoder_to_image(self.inner.as_mut(), layout)?;
382+
383+
// Apply the profile. If the profile itself is not valid or not present you get the default
384+
// presumption: `sRGB`. Otherwise we will try to make sense of the profile and if it is not
385+
// RGB we'll treat it as unspecified so that downstream will know that our handling of this
386+
// _existing_ profile was not / could not be done with full fidelity.
387+
if let Some(icc) = self.inner.icc_profile()? {
388+
if let Some(cicp) = crate::metadata::cms_provider().parse_icc(&icc) {
389+
// We largely ignore the error itself here, you just get the image with no color
390+
// space attached to it.
391+
if let Ok(rgb) = cicp.try_into_rgb() {
392+
image.set_rgb_primaries(rgb.primaries);
393+
image.set_transfer_function(rgb.transfer);
394+
} else {
395+
image.set_rgb_primaries(crate::metadata::CicpColorPrimaries::Unspecified);
396+
image.set_transfer_function(
397+
crate::metadata::CicpTransferCharacteristics::Unspecified,
398+
);
399+
}
400+
}
401+
}
402+
403+
Ok(if let Some(vb) = residual_vb {
404+
// Crop the image. This re-allocates a completely new buffer. For other types it may be
405+
// possible to do so with less work but not the normalized `ImageBuffer`.
406+
image.crop_imm(vb.x, vb.y, vb.width, vb.height)
407+
} else {
408+
image
409+
})
410+
}
411+
412+
/// Query the layout that the image will have.
413+
pub fn layout(&mut self) -> ImageResult<crate::ImageLayout> {
414+
self.inner.next_layout()
415+
}
416+
417+
/// Set the viewbox to apply when decoding images.
418+
pub fn set_viewbox(&mut self, viewbox: crate::math::Rect) {
419+
self.viewbox = Some(viewbox);
369420
}
370421

422+
/// Get the previously decoded EXIF metadata if any.
371423
pub fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
372424
self.inner.exif_metadata()
373425
}
374426

427+
/// Get the previously decoded ICC profile if any.
375428
pub fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
376429
self.inner.icc_profile()
377430
}
378431

432+
/// Get the previously decoded XMP metadata if any.
379433
pub fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
380434
self.inner.xmp_metadata()
381435
}
382436

437+
/// Get the previously decoded IPTC metadata if any.
383438
pub fn iptc_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
384439
self.inner.iptc_metadata()
385440
}

0 commit comments

Comments
 (0)