mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-26 11:09:23 -06:00
Merge PR #508
This commit is contained in:
@@ -1166,6 +1166,30 @@ mod tests {
|
||||
assert!(e.contains("without an embedded preview"), "{e}");
|
||||
}
|
||||
|
||||
/// A GoPro GPR (DNG with VC-5 coded data, no preview anywhere) still imports, described from its
|
||||
/// headers; loading it says what is wrong instead of naming a bare TIFF compression number.
|
||||
#[test]
|
||||
fn gopro_vc5_dng_imports_and_fails_to_load_with_a_clear_reason() {
|
||||
use lightcraft_tiff::{IfdBuilder, ImageData, TiffWriter, Value, tags as t};
|
||||
let mut ifd = IfdBuilder::new();
|
||||
ifd.set(t::NEW_SUBFILE_TYPE, Value::Long(vec![0]));
|
||||
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![32]));
|
||||
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![16]));
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
|
||||
ifd.set(t::SAMPLES_PER_PIXEL, Value::Short(vec![1]));
|
||||
ifd.set(t::PHOTOMETRIC, Value::Short(vec![32803]));
|
||||
ifd.set(t::COMPRESSION, Value::Short(vec![9]));
|
||||
ifd.set(t::DNG_VERSION, Value::Byte(vec![1, 4, 0, 0]));
|
||||
ifd.set(t::CFA_REPEAT_PATTERN_DIM, Value::Short(vec![2, 2]));
|
||||
ifd.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
|
||||
ifd.set_image(ImageData::Tiles { tile_width: 32, tile_height: 16, tiles: vec![vec![0x5au8; 400]] });
|
||||
let f = TiffWriter::default().write(&[ifd]).unwrap();
|
||||
let p = probe_bytes("GOPR0001.GPR", &f).unwrap();
|
||||
assert_eq!((p.kind, p.preview_only, p.width, p.height), (MediaKind::Raw, None, 32, 16));
|
||||
let e = load_bytes(&f, 16).unwrap_err();
|
||||
assert!(e.contains("GoPro VC-5") && e.contains("not decoded yet"), "{e}");
|
||||
}
|
||||
|
||||
/// Any failure of a recognised raw (not only an unsupported variant) can fall back to its preview;
|
||||
/// CR3 keeps its own wording.
|
||||
#[test]
|
||||
|
||||
@@ -125,6 +125,15 @@ pub(crate) fn decode_as(bytes: &[u8], mode: Mode, format: RawFormat) -> Result<R
|
||||
let tiff = Tiff::parse(bytes)?;
|
||||
let ifd0 = &tiff.ifds[0];
|
||||
let raw = raw_ifd(&tiff).ok_or_else(|| RawError::Corrupt("DNG without a raw image IFD".into()))?;
|
||||
// GoPro's GPR: a DNG whose raw data is VC-5 coded, which is not decoded (yet). Named here, before
|
||||
// the generic "TIFF compression 9" of the chunk decoder. Header mode still describes the file
|
||||
// (a GPR has no embedded preview, and a library keeps the photo with this reason shown, as it
|
||||
// does for any file whose pixels fail to decode, rather than refusing to import it).
|
||||
if mode == Mode::Full
|
||||
&& let Some(c) = raw.u16(t::COMPRESSION).filter(|&c| c == t::compression::VC5)
|
||||
{
|
||||
return Err(RawError::Unsupported(format!("DNG compression {c} (GoPro VC-5) is not decoded yet")));
|
||||
}
|
||||
let info = raw.image()?;
|
||||
let (w, h, cpp) = (info.width as usize, info.height as usize, info.samples_per_pixel as usize);
|
||||
if !(1..=4).contains(&cpp) {
|
||||
|
||||
@@ -942,6 +942,32 @@ mod tests {
|
||||
assert_eq!(probe(&g), None);
|
||||
}
|
||||
|
||||
/// A GoPro GPR (a DNG with VC-5 coded raw data, Compression 9) is refused by the full decode with
|
||||
/// a reason that names the format, whatever its tile bytes hold; the header probe still
|
||||
/// describes it (so it imports).
|
||||
#[test]
|
||||
fn gopro_vc5_dng_is_unsupported_with_a_clear_reason() {
|
||||
let mut ifd = IfdBuilder::new();
|
||||
ifd.set(t::NEW_SUBFILE_TYPE, Value::Long(vec![0]));
|
||||
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![32]));
|
||||
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![16]));
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
|
||||
ifd.set(t::SAMPLES_PER_PIXEL, Value::Short(vec![1]));
|
||||
ifd.set(t::PHOTOMETRIC, Value::Short(vec![32803]));
|
||||
ifd.set(t::COMPRESSION, Value::Short(vec![9]));
|
||||
ifd.set(t::DNG_VERSION, Value::Byte(vec![1, 4, 0, 0]));
|
||||
ifd.set(t::CFA_REPEAT_PATTERN_DIM, Value::Short(vec![2, 2]));
|
||||
ifd.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
|
||||
ifd.set_image(ImageData::Tiles { tile_width: 32, tile_height: 16, tiles: vec![vec![0x5au8; 400]] });
|
||||
let bytes = write(&[ifd]);
|
||||
assert_eq!(probe(&bytes), Some(RawFormat::Dng));
|
||||
let want = "DNG compression 9 (GoPro VC-5) is not decoded yet";
|
||||
let Err(RawError::Unsupported(why)) = decode(&bytes) else { panic!("expected Unsupported from decode") };
|
||||
assert_eq!(why, want);
|
||||
let info = probe_info(&bytes).expect("header probe describes the file");
|
||||
assert_eq!((info.width, info.height), (32, 16));
|
||||
}
|
||||
|
||||
/// Compression 99 (not a registered TIFF value; Leaf MOS tiles) marks a raw even without a CFA tag.
|
||||
#[test]
|
||||
fn private_compression_99_is_a_raw() {
|
||||
|
||||
@@ -200,6 +200,8 @@ pub mod compression {
|
||||
/// DNG lossy JPEG (baseline DCT, 8-bit).
|
||||
pub const LOSSY_JPEG: u16 = 34892;
|
||||
pub const JPEG_XL: u16 = 52546;
|
||||
/// SMPTE ST 2073 (VC-5) as written by GoPro's GPR files (the value measured in them).
|
||||
pub const VC5: u16 = 9;
|
||||
/// Nikon NEF Huffman compression.
|
||||
pub const NIKON: u16 = 34713;
|
||||
/// Sony ARW compressed (the value used by ARW files).
|
||||
|
||||
Reference in New Issue
Block a user