This is an automated email from the ASF dual-hosted git repository. kinow pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-imaging.git
commit e40f0db1dec673bf47f80d5827d87850ec0af0dc Author: gwlucastrig <[email protected]> AuthorDate: Sun Sep 12 14:56:47 2021 -0400 [IMAGING-312] Corrected handling of ExtraSamples tag --- .../imaging/formats/tiff/TiffImageParser.java | 36 ++++++-- .../formats/tiff/TiffAlphaRoundTripTest.java | 101 +++++++++++++++++++-- 2 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java b/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java index 4a8279f..5dec728 100644 --- a/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java +++ b/src/main/java/org/apache/commons/imaging/formats/tiff/TiffImageParser.java @@ -623,17 +623,35 @@ public class TiffImageParser extends ImageParser implements XmpEmbeddable { final int photometricInterpretation = 0xffff & directory.getFieldValue( TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION); - final boolean hasAlpha = - photometricInterpretation == TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_RGB - && samplesPerPixel==4; + boolean hasAlpha = false; boolean isAlphaPremultiplied = false; - if(hasAlpha){ - final TiffField extraSamplesField = - directory.findField(TiffTagConstants.TIFF_TAG_EXTRA_SAMPLES); - if (extraSamplesField != null) { + if (photometricInterpretation == TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_RGB + && samplesPerPixel == 4) { + final TiffField extraSamplesField + = directory.findField(TiffTagConstants.TIFF_TAG_EXTRA_SAMPLES); + if (extraSamplesField == null) { + // this state is not defined in the TIFF specification + // and so this code will interpret it as meaning that the + // proper handling would be ARGB. + hasAlpha = true; + isAlphaPremultiplied = false; + } else { final int extraSamplesValue = extraSamplesField.getIntValue(); - isAlphaPremultiplied = - (extraSamplesValue==TiffTagConstants.EXTRA_SAMPLE_ASSOCIATED_ALPHA); + switch (extraSamplesValue) { + case TiffTagConstants.EXTRA_SAMPLE_UNASSOCIATED_ALPHA: + hasAlpha = true; + isAlphaPremultiplied = false; + break; + case TiffTagConstants.EXTRA_SAMPLE_ASSOCIATED_ALPHA: + hasAlpha = true; + isAlphaPremultiplied = true; + break; + case 0: + default: + hasAlpha = false; + isAlphaPremultiplied = false; + break; + } } } diff --git a/src/test/java/org/apache/commons/imaging/formats/tiff/TiffAlphaRoundTripTest.java b/src/test/java/org/apache/commons/imaging/formats/tiff/TiffAlphaRoundTripTest.java index 20f0032..dfd1170 100644 --- a/src/test/java/org/apache/commons/imaging/formats/tiff/TiffAlphaRoundTripTest.java +++ b/src/test/java/org/apache/commons/imaging/formats/tiff/TiffAlphaRoundTripTest.java @@ -16,20 +16,28 @@ */ package org.apache.commons.imaging.formats.tiff; +import org.apache.commons.imaging.ImageFormats; +import org.apache.commons.imaging.Imaging; +import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; +import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy; +import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory; +import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileOutputStream; +import java.nio.ByteOrder; import java.nio.file.Path; import java.util.HashMap; -import org.apache.commons.imaging.ImageFormats; -import org.apache.commons.imaging.Imaging; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; -import org.junit.jupiter.api.io.TempDir; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; /** * Performs a round-trip that writes an image containing Alpha and then reads it @@ -131,4 +139,85 @@ public class TiffAlphaRoundTripTest { } return delta < iTolerance; } + + @Test + void testExtraSamples() throws Exception{ + + final int bytesPerSample = 4; + final int width = 10; + final int height = 10; + final int nBytesPerStrip = bytesPerSample * height * width; + final ByteOrder byteOrder = ByteOrder.nativeOrder(); + + int[] samples = new int[width * height]; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + int index = i * width + j; + samples[index] = j > i ? 0xffff0000 : 0x88ff0000; + } + } + + for (int iExtra = 0; iExtra < 3; iExtra++) { + final TiffOutputSet outputSet = new TiffOutputSet(byteOrder); + final TiffOutputDirectory outDir = outputSet.addRootDirectory(); + outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width); + outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height); + outDir.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL, (short) 4); + outDir.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, new short[]{8, 8, 8, 8}); + outDir.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION, + (short) TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_RGB); + outDir.add(TiffTagConstants.TIFF_TAG_COMPRESSION, + (short) TiffTagConstants.COMPRESSION_VALUE_UNCOMPRESSED); + outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION, + (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY); + outDir.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP, height); + outDir.add(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS, nBytesPerStrip); + + outDir.add(TiffTagConstants.TIFF_TAG_EXTRA_SAMPLES, (short) iExtra); + + final byte[] b = new byte[nBytesPerStrip]; + int k = 0; + for (int sample : samples) { + b[k++] = (byte) ((sample >> 16) & 0xff); // R + b[k++] = (byte) ((sample >> 8) & 0xff); // G + b[k++] = (byte) (sample & 0xff); // B + b[k++] = (byte) ((sample >> 24) & 0xff); // A + } + + final TiffElement.DataElement[] imageData = new TiffElement.DataElement[1]; + imageData[0] = new TiffImageData.Data(0, b.length, b); + + TiffImageData tiffImageData + = new TiffImageData.Strips(imageData, height); + + outDir.setTiffImageData(tiffImageData); + + final File outputFile = new File(tempDir.toFile(), "TestExtraSamples" + iExtra + ".tiff"); + try (FileOutputStream fos = new FileOutputStream(outputFile); + BufferedOutputStream bos = new BufferedOutputStream(fos)) { + final TiffImageWriterLossy writer = new TiffImageWriterLossy(byteOrder); + writer.write(bos, outputSet); + bos.flush(); + } + + BufferedImage result = Imaging.getBufferedImage(outputFile); + int []argb = new int[samples.length]; + result.getRGB(0, 0, width, height, argb, 0, width); + int index = 3*width+1; + int iSample = samples[index]; + int iArgb = argb[index]; + if (iExtra == 0) { + // when extra samples is zero, the alpha channel is ignored. + // We expect ARGB to start with 0xff. So we OR in 0xff for + // the alpha value of the sample + iSample |= 0xff000000; + } else if (iExtra==1) { + // The pre-multiply alpha case + iSample = 0x89de0000; + } + String p = String.format("%08x", iSample); + String q = String.format("%08x", iArgb); + assertEquals(p, q, "Failure on ExtraSamples="+iExtra); + } + } }
