From 2eef9ac088209a8d21c89f9dce0884aed8356dee Mon Sep 17 00:00:00 2001 From: "A. Molzer" <5550310+197g@users.noreply.github.com> Date: Sun, 9 Nov 2025 07:04:58 +0100 Subject: [PATCH] Encoding of extra samples, roundtrip test --- src/encoder/mod.rs | 36 ++++++++++++++++++++++++--- tests/decode_images.rs | 32 ++++++++++++++++++------ tests/encode_images.rs | 2 +- tests/images/extra_bits_gray_8b.tiff | Bin 0 -> 338 bytes tests/images/extra_bits_rgb_8b.tiff | Bin 0 -> 482 bytes 5 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 tests/images/extra_bits_gray_8b.tiff create mode 100644 tests/images/extra_bits_rgb_8b.tiff diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 7ba03cf..6c1a965 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -563,9 +563,6 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, encoder.write_tag(Tag::Compression, compression.tag().to_u16())?; encoder.write_tag(Tag::Predictor, predictor.to_u16())?; - encoder.write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; - let sample_format: Vec<_> = ::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect(); - encoder.write_tag(Tag::SampleFormat, &sample_format[..])?; encoder.write_tag(Tag::PhotometricInterpretation, ::TIFF_VALUE.to_u16())?; encoder.write_tag(Tag::RowsPerStrip, u32::try_from(rows_per_strip)?)?; @@ -754,6 +751,39 @@ impl<'a, W: 'a + Write + Seek, T: ColorType, K: TiffKind> ImageEncoder<'a, W, T, } fn finish_internal(&mut self) -> TiffResult> { + if self.extra_samples.is_empty() { + self.encoder + .write_tag(Tag::BitsPerSample, ::BITS_PER_SAMPLE)?; + } else { + let mut sample_format: Vec<_> = ::BITS_PER_SAMPLE.to_vec(); + let replicated = + core::iter::repeat(::BITS_PER_SAMPLE[0]).take(self.extra_samples.len()); + sample_format.extend(replicated); + + self.encoder + .write_tag(Tag::BitsPerSample, &sample_format[..])?; + + self.encoder.write_tag( + Tag::SamplesPerPixel, + u16::try_from(::BITS_PER_SAMPLE.len() + self.extra_samples.len())?, + )?; + } + + let mut sample_format: Vec<_> = ::SAMPLE_FORMAT.iter().map(|s| s.to_u16()).collect(); + let extra_format = sample_format + .first() + .copied() + // Frankly should not occur, we have no sample format without at least one entry. We + // would need an interface upgrade to handle this however. Either decide to support + // heterogeneous sample formats or a way to provide fallback that is used for purely + // extra samples and not provided via a slice used for samples themselves. + .unwrap_or(SampleFormat::Void.to_u16()); + + sample_format.extend(core::iter::repeat(extra_format).take(self.extra_samples.len())); + + self.encoder + .write_tag(Tag::SampleFormat, &sample_format[..])?; + self.encoder .write_tag(Tag::StripOffsets, K::convert_slice(&self.strip_offsets))?; self.encoder.write_tag( diff --git a/tests/decode_images.rs b/tests/decode_images.rs index e1e55f5..5448686 100644 --- a/tests/decode_images.rs +++ b/tests/decode_images.rs @@ -686,19 +686,37 @@ fn test_fax4_white_is_min() { } #[test] -fn extra_bits_image() { +fn write_extra_bits_image() { use tiff::{encoder, tags::ExtraSamples}; - let path = PathBuf::from(TEST_IMAGE_DIR).join("extra_bits_gray_4b.tiff"); + let path = PathBuf::from(TEST_IMAGE_DIR).join("extra_bits_rgb_8b.tiff"); let img_file = File::create(path).expect("Cannot find test image!"); let mut encoder = encoder::TiffEncoder::new(img_file).expect("Cannot create encoder"); - let mut img = encoder - .new_image::(8, 8) - .unwrap(); + let mut img = encoder.new_image::(8, 8).unwrap(); img.extra_samples(&[ExtraSamples::Unspecified]).unwrap(); - let data: [u8; 128] = core::array::from_fn(|i| i as u8 % 2); - img.write_data(&data).expect("correct amount of data to write"); + // all pixels are supposed to be 0, the extra is 1 + let data: [u8; 256] = core::array::from_fn(|i| (i as u8 % 4).saturating_sub(2)); + + img.write_data(&data) + .expect("correct amount of data to write"); +} + +#[test] +fn extra_bits_gray() { + test_image_sum_u8( + "extra_bits_gray_8b.tiff", + ColorType::Multiband { + bit_depth: 8, + num_samples: 2, + }, + 64, + ); +} + +#[test] +fn extra_bits_rgb() { + test_image_sum_u8("extra_bits_rgb_8b.tiff", ColorType::RGB(8), 0); } diff --git a/tests/encode_images.rs b/tests/encode_images.rs index d16bf69..b16e87d 100644 --- a/tests/encode_images.rs +++ b/tests/encode_images.rs @@ -57,7 +57,7 @@ fn encode_decode() { ), (Tag::Compression, ifd::Value::Short(1)), (Tag::PhotometricInterpretation, ifd::Value::Short(2)), - (Tag::StripOffsets, ifd::Value::Unsigned(47)), + (Tag::StripOffsets, ifd::Value::Unsigned(35)), (Tag::SamplesPerPixel, ifd::Value::Short(3)), (Tag::RowsPerStrip, ifd::Value::Unsigned(3334)), (Tag::StripByteCounts, ifd::Value::Unsigned(30000)), diff --git a/tests/images/extra_bits_gray_8b.tiff b/tests/images/extra_bits_gray_8b.tiff new file mode 100644 index 0000000000000000000000000000000000000000..dd8b0b795ca8fa50035e46ef295643372db5f479 GIT binary patch literal 338 zcmebD)MA*yz`($WhJgYD3i*LfVgb8~1LQI!HWMQ=0~1h;0|=RcY?#a0kl2Dy^%6jJ zqEK;=9x>wx` RsDvRH$Of6M0CETr003A81wH@( literal 0 HcmV?d00001