mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-29 04:27:30 -06:00
Merge PR #1412
This commit is contained in:
@@ -223,6 +223,13 @@ impl DecodedSource {
|
||||
#[derive(Clone)]
|
||||
pub enum SourceRef {
|
||||
Loaded(Box<DecodedSource>),
|
||||
/// A larger decoded source of the photo, fitted to `max_edge` by the worker: the thumbnail of
|
||||
/// the photo open in the loupe is cut from its preview-level source instead of decoding the
|
||||
/// file again (a second or more for a 64 MP raw).
|
||||
Fit {
|
||||
from: Box<DecodedSource>,
|
||||
max_edge: usize,
|
||||
},
|
||||
Demo {
|
||||
scene: Box<lightcraft_scenes::Scene>,
|
||||
max_edge: usize,
|
||||
@@ -251,6 +258,13 @@ impl SourceRef {
|
||||
pub fn load_source(&self) -> Result<DecodedSource, String> {
|
||||
let image = match self {
|
||||
SourceRef::Loaded(a) => return Ok((**a).clone()),
|
||||
SourceRef::Fit { from, max_edge } => {
|
||||
if from.image.width.max(from.image.height) <= *max_edge {
|
||||
return Ok((**from).clone());
|
||||
}
|
||||
let image = lightcraft_raster::resample::fit(&from.image, *max_edge, *max_edge, lightcraft_raster::resample::Filter::Box);
|
||||
return Ok(DecodedSource { image: Arc::new(image), denoised: None, ..(**from).clone() });
|
||||
}
|
||||
SourceRef::Demo { scene, max_edge } => crate::demo::scene_pixels(scene, *max_edge)?,
|
||||
SourceRef::File { path, max_edge, loader, fallback, denoise } => {
|
||||
let plain = || match loader {
|
||||
@@ -625,6 +639,15 @@ impl MediaCache {
|
||||
if cached && let Some(a) = self.get_source(p.id, level) {
|
||||
return SourceRef::Loaded(Box::new(a));
|
||||
}
|
||||
// a thumbnail of a photo whose preview is decoded (the one in the loupe) is cut from it; not
|
||||
// with a denoised picture, which the thumbnail would lose
|
||||
if cached
|
||||
&& level == SourceLevel::Thumb
|
||||
&& self.denoise.spec(p.id).is_none()
|
||||
&& let Some(big) = self.get_source(p.id, SourceLevel::Preview)
|
||||
{
|
||||
return SourceRef::Fit { from: Box::new(big), max_edge: level.max_edge() };
|
||||
}
|
||||
// Procedural scenes have a nominal size: "full" is that size, not unbounded.
|
||||
let max_edge = level.max_edge().min(match (&p.source, level) {
|
||||
(Source::Demo { .. }, SourceLevel::Full) => p.width.max(p.height).max(1) as usize,
|
||||
@@ -1987,6 +2010,24 @@ mod tests {
|
||||
assert!(mean(&a) > mean(&b) + 10.0, "two stops brighter after: {} vs {}", mean(&a), mean(&b));
|
||||
}
|
||||
|
||||
/// The thumbnail of a photo whose preview-level source is decoded is cut from it: the file (here
|
||||
/// one nothing can decode) is not read again.
|
||||
#[test]
|
||||
fn a_thumbnail_is_cut_from_the_decoded_preview() {
|
||||
let mut s = crate::Session::new();
|
||||
let p = Photo::new(PhotoId(1), Source::File { path: "/nowhere/a.ARW".into() }, "a.ARW", "ARW", 2400, 1600, "");
|
||||
s.catalog.apply(lightcraft_catalog::Op::AddPhoto { photo: Box::new(p.clone()) }).unwrap();
|
||||
s.media.insert(p.id, SourceLevel::Preview, Arc::new(Rgb32f::from_fn(2400, 1600, |x, _| [x as f32 / 2400.0, 0.5, 0.25])));
|
||||
let job = s.thumb_job(p.id, 512).unwrap();
|
||||
assert!(matches!(job.source, SourceRef::Fit { .. }));
|
||||
let r = job.run();
|
||||
let thumb = r.rendered.as_ref().unwrap();
|
||||
assert_eq!((thumb.image.width, thumb.image.height), (512, 341));
|
||||
s.accept(&r);
|
||||
let cached = s.media.get(p.id, SourceLevel::Thumb).unwrap();
|
||||
assert_eq!((cached.width, cached.height), (512, 341), "a thumbnail-sized source is kept, not the preview");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_source_follows_what_the_session_accepted() {
|
||||
let mut s = crate::Session::with_demo();
|
||||
|
||||
Reference in New Issue
Block a user