View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.formats.tiff;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  
21  import java.awt.image.BufferedImage;
22  import java.io.File;
23  import java.io.IOException;
24  
25  import org.apache.commons.imaging.Imaging;
26  import org.apache.commons.imaging.ImagingTestConstants;
27  import org.junit.jupiter.api.Test;
28  
29  /**
30   * Reads files in the BigTIFF samples folder and compares the BigTIFF files against the standard "classic" tiff image.
31   */
32  public class TiffJpegTest extends TiffBaseTest {
33  
34      static final String[] testSet0 = { "TestSample.tiff", "TestJpegStrips.tiff", "TestJpegTiles.tiff", "TestJpegStripsRgb.tiff", "TestJpegTilesRgb.tiff",
35              "TestJpegCMYK.tiff" };
36  
37      static final String[] testSet1 = { "TestSample264x264.tiff", "TestJpegStrips264x264.tiff", "TestJpegStrips264x264BigEndian.tiff",
38              "TestJpegTiles264x264.tiff", "TestJpegTiles264x264BigEndian.tiff" };
39  
40      static final String[] testSet2 = { "TestSampleArgb.tiff", "TestJpegArgb.tiff" };
41  
42      static final String[] testSet3 = { "TestSampleRgb127x127.tiff", "TestJpegRgb127x127.tiff" };
43  
44      private File getTiffFile(final String name) {
45          final File tiffFolder = new File(ImagingTestConstants.TEST_IMAGE_FOLDER, "tiff");
46          final File tiffJpegFolder = new File(tiffFolder, "14");
47          return new File(tiffJpegFolder, name);
48      }
49  
50      private int gray(final int argb) {
51          final int r = argb >> 16 & 0xff;
52          final int g = argb >> 8 & 0xff;
53          final int b = argb & 0xff;
54          return (int) (0.299 * r + 0.587 * g + 0.114 * b + 0.5);
55      }
56  
57      void performSubImageTest(final String name) throws IOException {
58          // get the full image to be used as a basis for comparison
59          final File target = getTiffFile(name);
60          final BufferedImage master = Imaging.getBufferedImage(target);
61          final int w = master.getWidth();
62          final int h = master.getHeight();
63          final int[] masterArgb = new int[w * h];
64          master.getRGB(0, 0, w, h, masterArgb, 0, w);
65  
66          // Get a subimage
67          final TiffImageParser tiffImageParser = new TiffImageParser();
68          final TiffImagingParameters params = new TiffImagingParameters();
69          final int testX0 = 11;
70          final int testY0 = 11;
71          final int testW = w - testX0 - 1;
72          final int testH = h - testY0 - 1;
73          params.setSubImage(testX0, testY0, testW, testH);
74  
75          final BufferedImage test = tiffImageParser.getBufferedImage(target, params);
76          final int[] testArgb = new int[testW * testH];
77          test.getRGB(0, 0, testW, testH, testArgb, 0, testW);
78          final String label = "Pixel mismatch for " + name;
79          for (int i = 0; i < testH; i++) {
80              for (int j = 0; j < testH; j++) {
81                  final int masterIndex = (i + testY0) * w + j + testX0;
82                  final int testIndex = i * testW + j;
83                  final int masterPixel = masterArgb[masterIndex];
84                  final int testPixel = testArgb[testIndex];
85                  assertEquals(masterPixel, testPixel, label);
86              }
87          }
88  
89      }
90  
91      /**
92       * Process a set of files using the first name in the array as the master image and comparing all subsequent files against its content on a pixel-by-pixel
93       * bases. The first name will usually be a TIFF file that is stored using a non-lossy compression format (something other than JPEG). All subsequent files
94       * will use JPEG based compression with an alternate geometry (strips, tiles, etc.). Because JPEG is lossy, we define a match as values of pixels that
95       * produce gray tones withing a threshold.
96       *
97       * @param testSet an array of 2 or more TIFF file names
98       * @throws IOException in the event of an unexpected I/O exception
99       */
100     private void processTestSet(final String[] testSet) throws IOException {
101         final File masterFile = getTiffFile(testSet[0]);
102         final BufferedImage masterImage = Imaging.getBufferedImage(masterFile);
103         final int w = masterImage.getWidth();
104         final int h = masterImage.getHeight();
105         final int[] masterArgb = new int[w * h];
106         masterImage.getRGB(0, 0, w, h, masterArgb, 0, w);
107 
108         final int[] masterGray = new int[w * h];
109         for (int i = 0; i < masterArgb.length; i++) {
110             masterGray[i] = gray(masterArgb[i]);
111         }
112 
113         for (int iFile = 1; iFile < testSet.length; iFile++) {
114             final String name = testSet[iFile];
115             final File f = getTiffFile(name);
116             final BufferedImage test = Imaging.getBufferedImage(f);
117             final int testW = test.getWidth();
118             final int testH = test.getHeight();
119             assertEquals(w, testW, "Width mismatch for " + name);
120             assertEquals(h, testH, "Height mismatch for " + name);
121 
122             final String label = "Pixel match for " + name;
123             final int[] testArgb = new int[w * h];
124             test.getRGB(0, 0, w, h, testArgb, 0, w);
125             for (int i = 0; i < testArgb.length; i++) {
126                 final int testGray = gray(testArgb[i]);
127                 final int masterA = masterArgb[i] >>> 24;
128                 final int testA = testArgb[i] >>> 24;
129                 assertEquals(masterA, testA, 1, label);
130                 assertEquals(masterGray[i], testGray, 4, label);
131             }
132         }
133     }
134 
135     /**
136      * Verify that TIFF files using JPEG compression provide a pixel-by-pixel match with source files that use a lossless compression.
137      */
138     @Test
139     public void testFullPixelMatch() throws IOException {
140 
141         // testSet1 is more comprehensive than testSet0 and
142         // would be sufficient to cover all cases. The test files for testSet0
143         // are simpler and might be more useful for debugging
144         processTestSet(testSet0);
145         processTestSet(testSet1);
146 
147         // test set 2 includes a TIFF-specific alpha channel
148         processTestSet(testSet2);
149 
150         // test set 3 includes an add-valued width and height that does
151         // not evently fit a block of JPEG data. It is used to test
152         // for the proper handling of edge-cases.
153         processTestSet(testSet3);
154     }
155 
156     /**
157      * Verify that a sub-image extraction supports a pixel-by-pixel match with the full image extraction.
158      */
159     @Test
160     public void testSubImage() throws IOException {
161         performSubImageTest("TestJpegProgressive.tiff"); // strips
162         performSubImageTest("TestJpegTiles264x264.tiff"); // tiles
163         performSubImageTest("TestJpegRgb127x127.tiff");
164     }
165 
166 }