mirror of
https://github.com/storytold/designcraft.git
synced 2026-11-14 22:35:26 +00:00
Estimate reduced JPEG warm reservations
This commit is contained in:
3 files changed
+38
-5
No files matched your search
@@ -65,6 +65,36 @@ pub(crate) fn decode_cmyk(bytes: &[u8]) -> Option<(CmykRaster, Option<Vec<u8>>)>
|
||||
/// The largest reduction [`decode_reduced`] reads straight from a JPEG's 8×8 blocks: 1/8.
|
||||
pub(crate) const MAX_SHRINK: u8 = 3;
|
||||
|
||||
/// Output size of a JPEG reduction without decoding pixels. `None` when the reduced decoder
|
||||
/// cannot handle the JPEG, including its full-size resource limits.
|
||||
pub(crate) fn reduced_size(bytes: &[u8], shrink: u8) -> Option<(u32, u32)> {
|
||||
use jpeg_decoder::{CodingProcess, PixelFormat};
|
||||
if !bytes.starts_with(&[0xFF, 0xD8]) || shrink == 0 || shrink > MAX_SHRINK {
|
||||
return None;
|
||||
}
|
||||
let mut d = jpeg_decoder::Decoder::new(bytes);
|
||||
d.set_max_decoding_buffer_size(MAX_BYTES);
|
||||
d.read_info().ok()?;
|
||||
let info = d.info()?;
|
||||
let colour = match info.pixel_format {
|
||||
PixelFormat::RGB24 => true,
|
||||
PixelFormat::L8 => false,
|
||||
PixelFormat::L16 | PixelFormat::CMYK32 => return None,
|
||||
};
|
||||
if crate::over_rgba_budget(u32::from(info.width), u32::from(info.height)) {
|
||||
return None;
|
||||
}
|
||||
if info.coding_process == CodingProcess::DctProgressive {
|
||||
let side = |n: u16| u64::from(n).next_multiple_of(32);
|
||||
let components: u64 = if colour { 3 } else { 1 };
|
||||
if side(info.width) * side(info.height) * components * 2 > MAX_BYTES as u64 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let reduced = |n: u16| u32::from(n).div_ceil(1 << shrink);
|
||||
Some((reduced(info.width), reduced(info.height)))
|
||||
}
|
||||
|
||||
/// A YCbCr, RGB or grayscale JPEG decoded at 1/2^`shrink` of its size, each side rounded up, for
|
||||
/// `shrink` 1 to [`MAX_SHRINK`]: straight RGBA and whether the image has colour. `None` for CMYK,
|
||||
/// 12-bit and lossless JPEGs, other formats and anything that doesn't decode. `None` as well for a
|
||||
|
||||
@@ -510,6 +510,12 @@ pub fn decode_rgba_reduced(bytes: &[u8], shrink: u8) -> Option<image::RgbaImage>
|
||||
to_display_rgba(px, Some(&icc), &DisplayColour::active())
|
||||
}
|
||||
|
||||
/// Dimensions of a JPEG decoded at `1/2^shrink`, without decoding its pixels. `None` when the
|
||||
/// reduced decoder cannot handle the image.
|
||||
pub fn reduced_jpeg_size(bytes: &[u8], shrink: u8) -> Option<(u32, u32)> {
|
||||
jpeg::reduced_size(bytes, shrink)
|
||||
}
|
||||
|
||||
/// False when `bytes` is a WebP whose `ICCP` chunk declares more bytes than the file holds or than
|
||||
/// [`colour::MAX_ICC_BYTES`]. The decoder sizes its buffer from the declared length before it reads
|
||||
/// anything, so such a file must not reach `icc_profile()`; its pixels decode without it.
|
||||
|
||||
@@ -200,14 +200,11 @@ fn reduced_estimates(wants: &[Want]) -> Option<(usize, usize)> {
|
||||
return None;
|
||||
}
|
||||
let shrink = level.min(usize::from(designcraft_images::MAX_REDUCED_SHRINK));
|
||||
let (decoded_w, decoded_h) = designcraft_images::reduced_jpeg_size(&w.bytes, shrink as u8)?;
|
||||
for cached in shrink..=level {
|
||||
levels[cached] = true;
|
||||
}
|
||||
let denom = 1u32 << shrink;
|
||||
let (w_px, h_px) = sizes[0];
|
||||
// The reduced decoder rounds each side up before the mip is cropped to `sizes[shrink]`.
|
||||
let decode_pixels = w_px.div_ceil(denom) as usize * h_px.div_ceil(denom) as usize;
|
||||
peak = peak.max(decode_pixels.saturating_mul(8));
|
||||
peak = peak.max(decoded_w as usize * decoded_h as usize * 8);
|
||||
if dims.as_ref().is_none_or(|d: &Vec<(u32, u32)>| d.len() < sizes.len()) {
|
||||
dims = Some(sizes);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user