TIFF: save uncompressed by default, like Photoshop (fixes #4484) (#4491)

This commit is contained in:
Marco Arnulfo authored and GitHub committed 2026-10-13 11:56:27 -04:00
1 parent cb6ccaafc4
commit 5c73f4fffa
2 files changed
+28 -3

No files matched your search

+4 -3
View File
@@ -100,12 +100,13 @@ pub enum PngCompression {
Best,
}
/// TIFF strip compression.
/// TIFF strip compression. The default is none, as in Photoshop: uncompressed strips are the
/// only kind every TIFF reader opens (Deflate and the predictor are not baseline TIFF, #4484).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TiffCompression {
#[default]
None,
Lzw,
#[default]
Deflate,
PackBits,
}
@@ -190,7 +191,7 @@ impl Default for EncodeOptions {
png_interlaced: false,
webp_lossless: true,
webp_quality: 80,
tiff_compression: TiffCompression::Deflate,
tiff_compression: TiffCompression::None,
tiff_bigtiff: false,
exr_compression: ExrCompression::Zip16,
dpx_bits: None,
+24
View File
@@ -150,3 +150,27 @@ fn ccitt_group4_tiff_decodes() {
assert_ne!(d[0], d[8], "{d:?}");
assert_eq!(d[0], 255, "white row of a WhiteIsZero image");
}
/// #4484: a TIFF saved with the default options must open in software that only reads
/// baseline TIFF (embroidery and plotter tools, older readers). Like Photoshop's default, that
/// means uncompressed strips with no predictor, classic offsets and unassociated alpha.
#[test]
fn default_tiff_is_baseline() {
use photocraft_codecs::{ChannelLayout, EncodeOptions, Format, SampleType, encode};
use std::io::Cursor;
use tiff::tags::Tag;
for layout in [ChannelLayout::Rgb, ChannelLayout::Rgba] {
let img = common::synth(33, 21, layout, SampleType::U8, 7, 0.4);
let bytes = encode(&img, Format::Tiff, &EncodeOptions::default()).expect("encode");
assert!(bytes.starts_with(b"II*\0") || bytes.starts_with(b"MM\0*"), "{layout:?}: not a classic TIFF");
let mut dec = tiff::decoder::Decoder::new(Cursor::new(&bytes[..])).expect("open");
assert_eq!(dec.get_tag_u32(Tag::Compression).ok(), Some(1), "{layout:?}: Compression");
assert_eq!(dec.find_tag(Tag::Predictor).ok().flatten().and_then(|v| v.into_u16().ok()).unwrap_or(1), 1, "{layout:?}: Predictor");
assert!(dec.find_tag(Tag::TileWidth).ok().flatten().is_none(), "{layout:?}: tiled");
assert_eq!(dec.get_tag_u32(Tag::PlanarConfiguration).ok().unwrap_or(1), 1, "{layout:?}: planar");
let extra = dec.find_tag(Tag::ExtraSamples).ok().flatten().and_then(|v| v.into_u16_vec().ok());
assert_eq!(extra, if layout == ChannelLayout::Rgba { Some(vec![2]) } else { None }, "{layout:?}: ExtraSamples");
assert_eq!(decode(&bytes).expect("decode").data(), img.data(), "{layout:?}: round trip");
}
}