mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
Merge pull request #53 from storytold/pr/never-crash-raw-tiff
Never crash: raw and tiff decoders
This commit is contained in:
12 files changed
+250
-16
No files matched your search
@@ -185,9 +185,8 @@ pub fn write_dng(raw: &RawImage, opts: &DngWriteOptions) -> Result<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
match &raw.data {
|
||||
RawData::F32(v) if matches!(opts.compression, DngCompression::Deflate { .. }) => {
|
||||
let DngCompression::Deflate { tile, half } = opts.compression else { unreachable!() };
|
||||
match (&raw.data, opts.compression) {
|
||||
(RawData::F32(v), DngCompression::Deflate { tile, half }) => {
|
||||
let tile = (tile.max(16) & !15) as usize;
|
||||
let bp = if half { 2 } else { 4 };
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![(bp * 8) as u16; cpp]));
|
||||
@@ -222,7 +221,7 @@ pub fn write_dng(raw: &RawImage, opts: &DngWriteOptions) -> Result<Vec<u8>> {
|
||||
.collect();
|
||||
ifd.set_image(ImageData::Tiles { tile_width: tile as u32, tile_height: tile as u32, tiles });
|
||||
}
|
||||
RawData::F32(v) => {
|
||||
(RawData::F32(v), _) => {
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![32; cpp]));
|
||||
ifd.set(t::SAMPLE_FORMAT, Value::Short(vec![3; cpp]));
|
||||
ifd.set(t::COMPRESSION, Value::Short(vec![compression::NONE]));
|
||||
@@ -234,7 +233,7 @@ pub fn write_dng(raw: &RawImage, opts: &DngWriteOptions) -> Result<Vec<u8>> {
|
||||
}
|
||||
ifd.set_image(ImageData::Strips { rows_per_strip: h as u32, strips: vec![bytes] });
|
||||
}
|
||||
RawData::U16(v) => {
|
||||
(RawData::U16(v), _) => {
|
||||
let maxv = v.iter().copied().max().unwrap_or(0);
|
||||
let bits = (16 - maxv.leading_zeros()).max(raw.bits.min(16)).max(2) as u8;
|
||||
ifd.set(
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//! (public specifications, tag-name documentation, black-box analysis of CC0 samples) and gaps. Non-DNG files carry no
|
||||
//! colour matrix: [`color`] falls back to a documented neutral model. The decoders never panic on malformed input.
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
mod binned;
|
||||
pub mod color;
|
||||
@@ -266,6 +267,11 @@ impl Cfa {
|
||||
.collect::<Option<_>>()?;
|
||||
(p.len() == 4).then_some(Cfa { width: 2, height: 2, pattern: p })
|
||||
}
|
||||
/// A 2×2 Bayer layout named in code (`"RGGB"`, `"BGGR"`, `"GRBG"`, `"GBRG"`). Only for literal
|
||||
/// names (tested below); anything else falls back to RGGB instead of failing.
|
||||
pub(crate) fn bayer_static(s: &'static str) -> Cfa {
|
||||
Cfa::bayer(s).unwrap_or(Cfa { width: 2, height: 2, pattern: vec![0, 1, 1, 2] })
|
||||
}
|
||||
/// The Fujifilm X-Trans 6×6 layout (as commonly documented), anchored at (0, 0).
|
||||
pub fn xtrans() -> Cfa {
|
||||
let rows = ["GGRGGB", "GGBGGR", "BRGRBG", "GGBGGR", "GGRGGB", "RBGBRG"];
|
||||
@@ -575,6 +581,9 @@ mod tests {
|
||||
assert_eq!(c.shifted(1, 0).name(), "GRBG");
|
||||
assert_eq!(c.shifted(1, 1).name(), "BGGR");
|
||||
assert!(Cfa::bayer("RGGX").is_none());
|
||||
for name in ["RGGB", "BGGR", "GRBG", "GBRG"] {
|
||||
assert_eq!(Cfa::bayer_static(name).name(), name);
|
||||
}
|
||||
let x = Cfa::xtrans();
|
||||
assert!(!x.is_bayer());
|
||||
let greens = x.pattern.iter().filter(|&&p| p == 1).count();
|
||||
|
||||
@@ -359,7 +359,11 @@ pub fn decode(d: &[u8], max_samples: usize) -> Result<Frame, RawError> {
|
||||
if (entropy.len() as u64 + 64) * 8 < total as u64 {
|
||||
return Err(err("entropy data too short for frame"));
|
||||
}
|
||||
let tables: Vec<&Huffman> = h.table_for.iter().map(|&t| h.tables[t].as_ref().expect("checked")).collect();
|
||||
let tables: Vec<&Huffman> = h
|
||||
.table_for
|
||||
.iter()
|
||||
.map(|&t| h.tables.get(t).and_then(Option::as_ref).ok_or_else(|| err("scan uses an undefined Huffman table")))
|
||||
.collect::<Result<_, RawError>>()?;
|
||||
let mut out = vec![0u16; total];
|
||||
let mut br = BitReader::new(entropy);
|
||||
let init = 1i32 << (h.precision - h.pt - 1);
|
||||
|
||||
Vendored
+3
-2
@@ -60,7 +60,8 @@ pub(crate) fn decode_row(row: &[u8], out: &mut [u16]) {
|
||||
while x0 + 32 <= w && x0 + 32 <= row.len() {
|
||||
for half in 0..2 {
|
||||
let off = x0 + half * 16;
|
||||
let block = u128::from_le_bytes(row[off..off + 16].try_into().expect("16 bytes"));
|
||||
let Some(bytes) = row.get(off..off + 16).and_then(|b| <[u8; 16]>::try_from(b).ok()) else { return };
|
||||
let block = u128::from_le_bytes(bytes);
|
||||
let max = (block & 0x7ff) as u16;
|
||||
let min = ((block >> 11) & 0x7ff) as u16;
|
||||
let imax = ((block >> 22) & 0xf) as usize;
|
||||
@@ -217,7 +218,7 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result<RawImage> {
|
||||
|
||||
let cfa = match (raw.u64s(t::CFA_REPEAT_PATTERN_DIM).as_deref(), raw.bytes(t::CFA_PATTERN_EP)) {
|
||||
(Some([2, 2]), Some(p)) if p.len() == 4 && p.iter().all(|&c| c <= 2) => Cfa { width: 2, height: 2, pattern: p.to_vec() },
|
||||
_ => Cfa::bayer("RGGB").expect("static"),
|
||||
_ => Cfa::bayer_static("RGGB"),
|
||||
};
|
||||
let default_black = if out_bits >= 14 { 512.0 } else { 128.0 };
|
||||
let black = match raw.f64s(BLACK_LEVEL).as_deref() {
|
||||
|
||||
Vendored
+1
-1
@@ -117,7 +117,7 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result<RawImage> {
|
||||
}
|
||||
}
|
||||
let wb = mn.as_ref().and_then(|m| m.ifd.u64s(COLOR_BALANCE)).and_then(|v| wb_from_color_balance(&v));
|
||||
let cfa = Cfa::bayer("RGGB").expect("static");
|
||||
let cfa = Cfa::bayer_static("RGGB");
|
||||
let black = if active.x >= 8 {
|
||||
black_from_columns(&data, width, 2..active.x - 2, active.y..active.y + active.height, active)
|
||||
} else {
|
||||
|
||||
Vendored
+1
-1
@@ -84,7 +84,7 @@ pub(crate) fn decode(bytes: &[u8]) -> Result<RawImage> {
|
||||
.map(|v| [v[0] as f32, 1.0, v[1] as f32]);
|
||||
let cfa = match (raw.u64s(t::CFA_REPEAT_PATTERN_DIM).as_deref(), raw.bytes(t::CFA_PATTERN_EP)) {
|
||||
(Some([2, 2]), Some(p)) if p.len() == 4 && p.iter().all(|&c| c <= 2) => Cfa { width: 2, height: 2, pattern: p.to_vec() },
|
||||
_ => Cfa::bayer("RGGB").expect("static"),
|
||||
_ => Cfa::bayer_static("RGGB"),
|
||||
};
|
||||
let white = white_from_data(samples, bits);
|
||||
let active_w = trailing_masked_columns(samples, w, h, white);
|
||||
|
||||
Vendored
+1
-1
@@ -79,7 +79,7 @@ pub(crate) fn cfa_from_data(d: &[u16], w: usize, a: Rect) -> Cfa {
|
||||
anti += (p(1, 0) - p(0, 1)).unsigned_abs();
|
||||
}
|
||||
}
|
||||
Cfa::bayer(if main < anti { "GRBG" } else { "RGGB" }).expect("static")
|
||||
Cfa::bayer_static(if main < anti { "GRBG" } else { "RGGB" })
|
||||
}
|
||||
|
||||
pub(crate) fn decode(bytes: &[u8]) -> Result<RawImage> {
|
||||
|
||||
Vendored
+1
-1
@@ -232,7 +232,7 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result<RawImage> {
|
||||
};
|
||||
let cfa = match (ifd0.u64s(t::CFA_REPEAT_PATTERN_DIM).as_deref(), ifd0.bytes(t::CFA_PATTERN_EP)) {
|
||||
(Some([2, 2]), Some(p)) if p.len() == 4 && p.iter().all(|&c| c <= 2) => Cfa { width: 2, height: 2, pattern: p.to_vec() },
|
||||
_ => Cfa::bayer("BGGR").expect("static"),
|
||||
_ => Cfa::bayer_static("BGGR"),
|
||||
};
|
||||
let black = match mn.as_ref().and_then(|m| m.ifd.f64s(BLACK_POINT)).as_deref() {
|
||||
Some(v @ [_, _, _, _]) => {
|
||||
|
||||
Vendored
+1
-1
@@ -91,7 +91,7 @@ fn raw_ifd(raw: &[u8]) -> Result<Ifd> {
|
||||
fn cfa(h: &Header) -> Cfa {
|
||||
match record(h, XTRANS_LAYOUT) {
|
||||
Some(l) if l.len() == 36 && l.iter().all(|&c| c <= 2) => Cfa { width: 6, height: 6, pattern: l.iter().rev().copied().collect() },
|
||||
_ => Cfa::bayer("RGGB").expect("static"),
|
||||
_ => Cfa::bayer_static("RGGB"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -74,11 +74,11 @@ pub(crate) fn unpack(stream: &[u8], bits: u32, out: &mut [u16]) -> Result<()> {
|
||||
let first_block = ci * 1024;
|
||||
for (bi, px) in o.chunks_mut(per).enumerate() {
|
||||
let at = (first_block + bi) * 16;
|
||||
let Some(b) = stream.get(at..at + 16) else {
|
||||
let Some(b) = stream.get(at..at + 16).and_then(|b| <[u8; 16]>::try_from(b).ok()) else {
|
||||
px.fill(0);
|
||||
continue;
|
||||
};
|
||||
let word = u128::from_le_bytes(b.try_into().expect("16 bytes"));
|
||||
let word = u128::from_le_bytes(b);
|
||||
for (i, p) in px.iter_mut().enumerate() {
|
||||
*p = ((word >> (start + i as u32 * bits)) & mask) as u16;
|
||||
}
|
||||
@@ -94,7 +94,7 @@ fn cfa(code: u16) -> Cfa {
|
||||
4 => "BGGR",
|
||||
_ => "RGGB",
|
||||
};
|
||||
Cfa::bayer(s).expect("static")
|
||||
Cfa::bayer_static(s)
|
||||
}
|
||||
|
||||
pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result<RawImage> {
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
//! Malformed vendor raw files (NEF, ARW, PEF, ORF, RW2, CR2, RAF) must decode to an error, never panic.
|
||||
|
||||
use lightcraft_tiff::tags as t;
|
||||
use lightcraft_tiff::{ByteOrder, IfdBuilder, ImageData, TiffWriter, Value};
|
||||
use proptest::prelude::*;
|
||||
|
||||
const W: u32 = 32;
|
||||
const H: u32 = 8;
|
||||
|
||||
/// A CFA TIFF the way most vendors lay it out: IFD0 (make) + a SubIFD holding the raw strip.
|
||||
fn cfa_tiff(make: &str, compression: u16, bits: u16, order: ByteOrder) -> Vec<u8> {
|
||||
let px = (W * H) as usize;
|
||||
// Sony's ARW2 (32767) packs a sample per byte
|
||||
let len = if compression == 32767 { px } else { px * 2 };
|
||||
let strip: Vec<u8> = (0..len).map(|i| (i * 37 % 251) as u8).collect();
|
||||
let mut raw = IfdBuilder::new();
|
||||
raw.set(t::NEW_SUBFILE_TYPE, Value::Long(vec![0]));
|
||||
raw.set(t::IMAGE_WIDTH, Value::Long(vec![W]));
|
||||
raw.set(t::IMAGE_LENGTH, Value::Long(vec![H]));
|
||||
raw.set(t::BITS_PER_SAMPLE, Value::Short(vec![bits]));
|
||||
raw.set(t::COMPRESSION, Value::Short(vec![compression]));
|
||||
raw.set(t::PHOTOMETRIC, Value::Short(vec![t::photometric::CFA]));
|
||||
raw.set(t::CFA_REPEAT_PATTERN_DIM, Value::Short(vec![2, 2]));
|
||||
raw.set(t::CFA_PATTERN_EP, Value::Byte(vec![0, 1, 1, 2]));
|
||||
raw.set_image(ImageData::Strips { rows_per_strip: H, strips: vec![strip] });
|
||||
let mut ifd0 = IfdBuilder::new();
|
||||
ifd0.set(t::MAKE, Value::Ascii(make.into()));
|
||||
ifd0.set(t::MODEL, Value::Ascii("TEST".into()));
|
||||
ifd0.add_sub_ifd(raw);
|
||||
TiffWriter::new(order, false).write(&[ifd0]).unwrap()
|
||||
}
|
||||
|
||||
/// An Olympus ORF (TIFF with an `IIRO` magic) or Panasonic RW2 (`IIU\0`), raw image in IFD0.
|
||||
fn magic_tiff(magic: &[u8; 4], make: &str) -> Vec<u8> {
|
||||
let px = (W * H) as usize;
|
||||
let mut ifd = IfdBuilder::new();
|
||||
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![W]));
|
||||
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![H]));
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
|
||||
ifd.set(t::COMPRESSION, Value::Short(vec![1]));
|
||||
ifd.set(t::PHOTOMETRIC, Value::Short(vec![1]));
|
||||
ifd.set(t::MAKE, Value::Ascii(make.into()));
|
||||
ifd.set_image(ImageData::Strips { rows_per_strip: H, strips: vec![(0..px * 2).map(|i| (i * 13) as u8).collect()] });
|
||||
let mut b = TiffWriter::new(ByteOrder::Little, false).write(&[ifd]).unwrap();
|
||||
b[..4].copy_from_slice(magic);
|
||||
b
|
||||
}
|
||||
|
||||
/// A Panasonic RW2: sensor size, bit depth and raw format in IFD0, 12-bit packed blocks.
|
||||
fn rw2() -> Vec<u8> {
|
||||
let mut ifd = IfdBuilder::new();
|
||||
ifd.set(0x0002, Value::Short(vec![W as u16]));
|
||||
ifd.set(0x0003, Value::Short(vec![H as u16]));
|
||||
ifd.set(0x0004, Value::Short(vec![1]));
|
||||
ifd.set(0x0005, Value::Short(vec![2]));
|
||||
ifd.set(0x0006, Value::Short(vec![H as u16 - 1]));
|
||||
ifd.set(0x0007, Value::Short(vec![W as u16 - 2]));
|
||||
ifd.set(0x000a, Value::Short(vec![12]));
|
||||
ifd.set(0x002d, Value::Short(vec![5]));
|
||||
ifd.set(0x001c, Value::Short(vec![64]));
|
||||
ifd.set(0x001d, Value::Short(vec![64]));
|
||||
ifd.set(0x001e, Value::Short(vec![64]));
|
||||
ifd.set(t::MAKE, Value::Ascii("Panasonic".into()));
|
||||
let need = (W * H).div_ceil(10) as usize * 16;
|
||||
ifd.set_image(ImageData::Strips { rows_per_strip: H, strips: vec![(0..need).map(|i| (i * 29) as u8).collect()] });
|
||||
let mut b = TiffWriter::new(ByteOrder::Little, false).write(&[ifd]).unwrap();
|
||||
b[..4].copy_from_slice(b"IIU\0");
|
||||
b
|
||||
}
|
||||
|
||||
/// A Pentax PEF: the raw image in IFD0, wide enough for the dark-border trim.
|
||||
fn pef(compression: u16) -> Vec<u8> {
|
||||
let (w, h) = (264u32, 4u32);
|
||||
let mut ifd = IfdBuilder::new();
|
||||
ifd.set(t::MAKE, Value::Ascii("PENTAX".into()));
|
||||
ifd.set(t::IMAGE_WIDTH, Value::Long(vec![w]));
|
||||
ifd.set(t::IMAGE_LENGTH, Value::Long(vec![h]));
|
||||
ifd.set(t::BITS_PER_SAMPLE, Value::Short(vec![16]));
|
||||
ifd.set(t::COMPRESSION, Value::Short(vec![compression]));
|
||||
ifd.set(t::PHOTOMETRIC, Value::Short(vec![t::photometric::CFA]));
|
||||
let strip: Vec<u8> = (0..(w * h * 2) as usize).map(|i| if i % 528 < 8 { 0 } else { (i * 7 % 200) as u8 }).collect();
|
||||
ifd.set_image(ImageData::Strips { rows_per_strip: h, strips: vec![strip] });
|
||||
TiffWriter::new(ByteOrder::Big, false).write(&[ifd]).unwrap()
|
||||
}
|
||||
|
||||
/// A Canon CR2: three placeholder IFDs and IFD3 with a 2-component lossless JPEG frame.
|
||||
fn cr2() -> Vec<u8> {
|
||||
let img: Vec<u16> = (0..(W * H) as usize).map(|i| 1024 + (i * 7919 % 12000) as u16).collect();
|
||||
let enc = lightcraft_raw::ljpeg::encode(&img, W as usize / 2, H as usize, 2, 14, 1, 0);
|
||||
let mut ifd0 = IfdBuilder::new();
|
||||
ifd0.set(t::MAKE, Value::Ascii("Canon".into()));
|
||||
let mut ifd3 = IfdBuilder::new();
|
||||
ifd3.set(t::COMPRESSION, Value::Short(vec![6]));
|
||||
ifd3.set(0xc640, Value::Short(vec![1, 16, 16]));
|
||||
ifd3.set_image(ImageData::Strips { rows_per_strip: H, strips: vec![enc] });
|
||||
let blank = || IfdBuilder::new().with(1, Value::Short(vec![0]));
|
||||
TiffWriter::new(ByteOrder::Little, false).write(&[ifd0, blank(), blank(), ifd3]).unwrap()
|
||||
}
|
||||
|
||||
/// A Fujifilm RAF: header, records, raw block (LE TIFF whose IFD0 points to a 0xf000 raw IFD).
|
||||
fn raf_file(w: u32, h: u32, bits: u32, strip: Vec<u8>, layout: Option<[u8; 36]>, jpeg: &[u8]) -> Vec<u8> {
|
||||
let mut block = b"II*\0\x08\0\0\0".to_vec();
|
||||
let sub_off: u32 = 8 + 2 + 12 + 4;
|
||||
block.extend_from_slice(&1u16.to_le_bytes());
|
||||
block.extend_from_slice(&[0x00, 0xf0, 13, 0, 1, 0, 0, 0]);
|
||||
block.extend_from_slice(&sub_off.to_le_bytes());
|
||||
block.extend_from_slice(&0u32.to_le_bytes());
|
||||
let n = 7u32;
|
||||
let wb_off = sub_off + 2 + 12 * n + 4;
|
||||
let strip_off = wb_off + 12;
|
||||
let entries: [(u16, u32, u32); 7] = [
|
||||
(0xf001, 1, w),
|
||||
(0xf002, 1, h),
|
||||
(0xf003, 1, bits),
|
||||
(0xf007, 1, strip_off),
|
||||
(0xf008, 1, strip.len() as u32),
|
||||
(0xf00a, 1, 64),
|
||||
(0xf00e, 3, wb_off),
|
||||
];
|
||||
block.extend_from_slice(&(n as u16).to_le_bytes());
|
||||
for (tag, count, v) in entries {
|
||||
block.extend_from_slice(&tag.to_le_bytes());
|
||||
block.extend_from_slice(&4u16.to_le_bytes());
|
||||
block.extend_from_slice(&count.to_le_bytes());
|
||||
block.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
block.extend_from_slice(&0u32.to_le_bytes());
|
||||
for v in [300u32, 600, 450] {
|
||||
block.extend_from_slice(&v.to_le_bytes());
|
||||
}
|
||||
block.extend_from_slice(&strip);
|
||||
let mut recs: Vec<u8> = Vec::new();
|
||||
let mut nrec = 0u32;
|
||||
let mut add = |tag: u16, d: &[u8]| {
|
||||
recs.extend_from_slice(&tag.to_be_bytes());
|
||||
recs.extend_from_slice(&(d.len() as u16).to_be_bytes());
|
||||
recs.extend_from_slice(d);
|
||||
nrec += 1;
|
||||
};
|
||||
add(0x0100, &[(h >> 8) as u8, h as u8, (w >> 8) as u8, w as u8]);
|
||||
add(0x0110, &[0, 2, 0, 4]);
|
||||
add(0x0111, &[0, (h - 2) as u8, 0, (w - 4) as u8]);
|
||||
if let Some(l) = layout {
|
||||
add(0x0131, &l);
|
||||
}
|
||||
let mut dir = nrec.to_be_bytes().to_vec();
|
||||
dir.extend_from_slice(&recs);
|
||||
let mut out = b"FUJIFILMCCD-RAW 0201FF000000TEST".to_vec();
|
||||
out.resize(108, 0);
|
||||
let jpeg_off = 160u32;
|
||||
let dir_off = jpeg_off + jpeg.len() as u32;
|
||||
let raw_off = dir_off + dir.len() as u32;
|
||||
for (i, v) in [jpeg_off, jpeg.len() as u32, dir_off, dir.len() as u32, raw_off, block.len() as u32].iter().enumerate() {
|
||||
out[84 + i * 4..88 + i * 4].copy_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
out.resize(160, 0);
|
||||
out.extend_from_slice(jpeg);
|
||||
out.extend_from_slice(&dir);
|
||||
out.extend_from_slice(&block);
|
||||
out
|
||||
}
|
||||
|
||||
fn raf() -> Vec<u8> {
|
||||
let strip: Vec<u8> = (0..(W * H * 2) as usize).map(|i| (i * 11) as u8).collect();
|
||||
let layout: [u8; 36] = std::array::from_fn(|i| (i % 3) as u8);
|
||||
raf_file(W, H, 16, strip, Some(layout), b"\xff\xd8\xff\xd9")
|
||||
}
|
||||
|
||||
fn samples() -> Vec<Vec<u8>> {
|
||||
vec![
|
||||
cfa_tiff("NIKON CORPORATION", 1, 12, ByteOrder::Big),
|
||||
cfa_tiff("NIKON CORPORATION", 34713, 14, ByteOrder::Big),
|
||||
cfa_tiff("SONY", 32767, 12, ByteOrder::Little),
|
||||
cfa_tiff("SONY", 1, 14, ByteOrder::Little),
|
||||
pef(1),
|
||||
pef(65535),
|
||||
cfa_tiff("SAMSUNG", 1, 12, ByteOrder::Little),
|
||||
cfa_tiff("ACME", 1, 16, ByteOrder::Little),
|
||||
magic_tiff(b"IIRO", "OLYMPUS IMAGING CORP."),
|
||||
rw2(),
|
||||
cr2(),
|
||||
raf(),
|
||||
]
|
||||
}
|
||||
|
||||
fn exercise(bytes: &[u8]) {
|
||||
let _ = lightcraft_raw::probe(bytes);
|
||||
let _ = lightcraft_raw::probe_info(bytes);
|
||||
let _ = lightcraft_raw::embedded_preview(bytes);
|
||||
if let Ok(img) = lightcraft_raw::decode(bytes)
|
||||
&& img.width * img.height <= 1 << 16
|
||||
{
|
||||
let _ = img.develop(lightcraft_raw::Method::Ahd);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vendor_samples_truncated_at_every_length() {
|
||||
for s in samples() {
|
||||
exercise(&s);
|
||||
for n in (0..s.len()).step_by(3) {
|
||||
exercise(&s[..n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#![proptest_config(ProptestConfig { cases: 2000, .. ProptestConfig::default() })]
|
||||
|
||||
#[test]
|
||||
fn mutated_vendor_files_never_panic(kind in 0usize..12, flips in proptest::collection::vec((any::<usize>(), any::<u8>()), 1..16), cut in any::<usize>()) {
|
||||
let mut data = samples().swap_remove(kind);
|
||||
let n = data.len();
|
||||
for (i, v) in flips {
|
||||
data[i % n] = v;
|
||||
}
|
||||
let keep = if cut % 3 == 0 { cut % n } else { n };
|
||||
exercise(&data[..keep]);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
//! Sources: TIFF 6.0 specification (Adobe, 1992), TIFF Technical Note / supplements, the BigTIFF
|
||||
//! design notes (AWare Systems), Adobe DNG specification 1.7 (tag numbers), CIPA DC-008 (Exif 2.32).
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
pub mod image;
|
||||
pub mod makernote;
|
||||
|
||||
Reference in new issue
Block a user