You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ki...@apache.org on 2021/09/12 21:20:39 UTC

[commons-imaging] 01/02: [IMAGING-312] Corrected handling of ExtraSamples tag

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 <co...@gmail.com>
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);
+        }
+    }
 }