commit
cba621e55c
|
|
@ -22,8 +22,9 @@ use lightcraft_raw::{RawFormat, RawImage, color::CameraTransform, profile::HsvTa
|
|||
/// that leave the fitted look alone. `RENDER_CACHE_VERSION` covers renders; this covers proxies.
|
||||
///
|
||||
/// 1: the fit as of #499's follow-up; 2: Sony DRO (tone curve lowered to Sony's curve without DRO)
|
||||
/// and the ILCE-7CR profile (#528, #583, #568, #616).
|
||||
pub const LOOK_VERSION: u32 = 2;
|
||||
/// and the ILCE-7CR profile (#528, #583, #568, #616); 3: the camera JPEG read in the colour space the raw's EXIF
|
||||
/// states, for every make (#778).
|
||||
pub const LOOK_VERSION: u32 = 3;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct CameraLook {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl SettingsHashes {
|
|||
}
|
||||
|
||||
/// Bump when the pipeline's output changes, to invalidate cached thumbnails.
|
||||
pub const RENDER_CACHE_VERSION: u64 = 50;
|
||||
pub const RENDER_CACHE_VERSION: u64 = 51;
|
||||
|
||||
/// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache).
|
||||
pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512];
|
||||
|
|
|
|||
|
|
@ -14,16 +14,35 @@ pub enum PreviewColorSpace {
|
|||
AdobeRgb,
|
||||
}
|
||||
|
||||
/// Nikon maker-note ColorSpace (0x001e: 1 = sRGB, 2 = Adobe RGB), per Nikon tag documentation.
|
||||
/// This is a fallback only: a JPEG's own ICC/EXIF colour declaration takes precedence.
|
||||
/// The colour space the raw says its embedded JPEG is in, for a JPEG that declares none itself (no ICC profile,
|
||||
/// no EXIF), first available first: the Nikon maker-note ColorSpace (0x001e: 1 = sRGB, 2 = Adobe RGB, per Nikon
|
||||
/// tag documentation); the raw's own EXIF ColorSpace (0xA001: 1 = sRGB), or Uncalibrated (0xFFFF) with the
|
||||
/// Interoperability IFD's InteropIndex "R03", the DCF option file for Adobe RGB (as a Sony body shot in Adobe RGB
|
||||
/// writes, issue #778; per the Exif and DCF specifications), not for a DNG. This is a fallback only: a JPEG's own ICC/EXIF colour
|
||||
/// declaration takes precedence.
|
||||
pub fn embedded_preview_color_space(bytes: &[u8]) -> Option<PreviewColorSpace> {
|
||||
let tiff = Tiff::parse(bytes).ok()?;
|
||||
let make = tiff.ifds.first()?.string(t::MAKE)?;
|
||||
if !make.to_ascii_uppercase().starts_with("NIKON") {
|
||||
let make = tiff.ifds.first()?.string(t::MAKE).unwrap_or_default();
|
||||
let exif = tiff.exif()?;
|
||||
if make.to_ascii_uppercase().starts_with("NIKON")
|
||||
&& let Some(space) = nikon_color_space(bytes, &tiff, exif, &make)
|
||||
{
|
||||
return Some(space);
|
||||
}
|
||||
// a DNG's EXIF describes the original capture; its previews are rendered by the converter
|
||||
if tiff.ifds.first()?.contains(t::DNG_VERSION) {
|
||||
return None;
|
||||
}
|
||||
let e = tiff.exif()?.get(t::MAKER_NOTE)?;
|
||||
let mn = makernote::parse_makernote(bytes, e.offset, e.count() as u64, tiff.order, &make)?;
|
||||
match exif.u16(0xA001)? {
|
||||
1 => Some(PreviewColorSpace::Srgb),
|
||||
0xFFFF if exif.interop.as_deref()?.bytes(0x0001)?.starts_with(b"R03") => Some(PreviewColorSpace::AdobeRgb),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn nikon_color_space(bytes: &[u8], tiff: &Tiff, exif: &Ifd, make: &str) -> Option<PreviewColorSpace> {
|
||||
let e = exif.get(t::MAKER_NOTE)?;
|
||||
let mn = makernote::parse_makernote(bytes, e.offset, e.count() as u64, tiff.order, make)?;
|
||||
match mn.ifd.u16(0x001e)? {
|
||||
1 => Some(PreviewColorSpace::Srgb),
|
||||
2 => Some(PreviewColorSpace::AdobeRgb),
|
||||
|
|
|
|||
|
|
@ -279,6 +279,45 @@ mod tests {
|
|||
assert_eq!(crate::embedded_preview_color_space(b"not a TIFF"), None);
|
||||
}
|
||||
|
||||
/// Issue #778: any make's raw falls back to its EXIF ColorSpace, with Uncalibrated + InteropIndex "R03" read as
|
||||
/// Adobe RGB; the Nikon note wins; a DNG is left alone.
|
||||
#[test]
|
||||
fn preview_color_space_falls_back_to_the_exif_color_space() {
|
||||
let file = |make: &str, cs: u16, interop: Option<&str>, note: Option<u16>, dng: bool| {
|
||||
let mut exif = IfdBuilder::new();
|
||||
exif.set(0xA001, Value::Short(vec![cs]));
|
||||
if let Some(i) = interop {
|
||||
let mut io = IfdBuilder::new();
|
||||
io.set(0x0001, Value::Ascii(i.into()));
|
||||
exif.set_child(t::INTEROP_IFD, io);
|
||||
}
|
||||
if let Some(v) = note {
|
||||
let mut mn = IfdBuilder::new();
|
||||
mn.set(0x001e, Value::Short(vec![v]));
|
||||
let mut bytes = b"Nikon | ||||