Verify WhiteIsZero invert, add Gray(1) color type

This was found through the fax4 decoding that is always bilevel.

No longer silently ignore an unknown color type when a photometric
interpretation requires an inversion. DICOM requires that WhiteIsZero
and BlackIsZero are only used for samples-per-pixel=1 but we do not
strictly enforce that early. Previously for the result Multiband color
we just ignored the tag which is also not right.
This commit is contained in:
Aurelia Molzer committed 2025-09-01 00:25:48 +02:00
1 parent 7c6c327817
commit bc71086a96
5 files changed
+33 -8

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@ half = { version = "2.4.1" }
quick-error = "2.0.1"
# Rename to avoid confusion with the feature name.
fax34 = { package = "fax", version = "0.2.4", optional = true }
fax34 = { package = "fax", version = "0.2.6", optional = true }
flate2 = { version = "1.0.20", optional = true }
weezl = { version = "0.1.10", optional = true }
zstd = { version = "0.13", optional = true }
+6 -3
View File
@@ -344,6 +344,9 @@ impl Image {
),
)),
},
// TODO: treatment of WhiteIsZero is not quite consistent with `invert_colors` that is
// later called when that interpretation is read. That function does not support
// Multiband as a color type and will error. It's unclear how to resolve that exactly.
PhotometricInterpretation::BlackIsZero | PhotometricInterpretation::WhiteIsZero => {
match self.samples {
1 => Ok(ColorType::Gray(self.bits_per_sample)),
@@ -684,7 +687,7 @@ impl Image {
);
}
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(tile, color_type, self.sample_format);
super::invert_colors(tile, color_type, self.sample_format)?;
}
} else if chunk_row_bytes > data_row_bytes && self.predictor == Predictor::FloatingPoint {
// The floating point predictor shuffles the padding bytes into the encoded output, so
@@ -701,7 +704,7 @@ impl Image {
_ => unreachable!(),
}
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(row, color_type, self.sample_format);
super::invert_colors(row, color_type, self.sample_format)?;
}
}
} else {
@@ -723,7 +726,7 @@ impl Image {
predictor,
);
if photometric_interpretation == PhotometricInterpretation::WhiteIsZero {
super::invert_colors(row, color_type, self.sample_format);
super::invert_colors(row, color_type, self.sample_format)?;
}
}
}
+20 -4
View File
@@ -475,11 +475,21 @@ fn fix_endianness_and_predict(
}
}
fn invert_colors(buf: &mut [u8], color_type: ColorType, sample_format: SampleFormat) {
fn invert_colors(
buf: &mut [u8],
color_type: ColorType,
sample_format: SampleFormat,
) -> TiffResult<()> {
match (color_type, sample_format) {
(ColorType::Gray(8), SampleFormat::Uint) => {
(ColorType::Gray(n), SampleFormat::Uint) if 8 % n == 0 => {
for x in buf {
*x = 0xff - *x;
// Equivalent to both of the following:
//
// *x = 0xff - *x
// *x = !*x
//
// since -x = !x+1
*x = !*x;
}
}
(ColorType::Gray(16), SampleFormat::Uint) => {
@@ -512,8 +522,14 @@ fn invert_colors(buf: &mut [u8], color_type: ColorType, sample_format: SampleFor
x.copy_from_slice(&(1.0 - v).to_ne_bytes());
}
}
_ => {}
_ => {
return Err(TiffError::UnsupportedError(
TiffUnsupportedError::UnknownInterpretation,
))
}
}
Ok(())
}
/// Fix endianness. If `byte_order` matches the host, then conversion is a no-op.
+6
View File
@@ -678,3 +678,9 @@ fn bytes_gray_f32() {
fn test_fax4() {
test_image_sum_u8("fax4.tiff", ColorType::Gray(1), 7802706);
}
#[test]
#[cfg(feature = "fax")]
fn test_fax4_white_is_min() {
test_image_sum_u8("imagemagick_group4.tiff", ColorType::Gray(1), 3742820);
}
Binary file not shown.