1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.tiff;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertThrows;
21
22 import java.io.BufferedOutputStream;
23 import java.io.File;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.nio.ByteOrder;
27 import java.nio.file.Path;
28
29 import org.apache.commons.imaging.FormatCompliance;
30 import org.apache.commons.imaging.ImagingException;
31 import org.apache.commons.imaging.bytesource.ByteSource;
32 import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
33 import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy;
34 import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
35 import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
36 import org.junit.jupiter.api.Test;
37 import org.junit.jupiter.api.io.TempDir;
38
39
40
41
42
43
44 public class TiffRoundTripInt32Test extends TiffBaseTest {
45
46 @TempDir
47 Path tempDir;
48
49 int width = 48;
50 int height = 23;
51
52 int[] sample = new int[width * height];
53
54 public TiffRoundTripInt32Test() {
55
56 for (int iCol = 0; iCol < width; iCol++) {
57 for (int iRow = 0; iRow < height; iRow++) {
58 final int index = iRow * width + iCol;
59 sample[index] = index - 10;
60 }
61 }
62 }
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 private byte[][] getBytesForOutput32(final int[] s, final int width, final int height, final int nRowsInBlock, final int nColsInBlock,
78 final ByteOrder byteOrder) {
79 final int nColsOfBlocks = (width + nColsInBlock - 1) / nColsInBlock;
80 final int nRowsOfBlocks = (height + nRowsInBlock + 1) / nRowsInBlock;
81 final int bytesPerPixel = 4;
82 final int nBlocks = nRowsOfBlocks * nColsOfBlocks;
83 final int nBytesInBlock = bytesPerPixel * nRowsInBlock * nColsInBlock;
84 final byte[][] blocks = new byte[nBlocks][nBytesInBlock];
85 for (int i = 0; i < height; i++) {
86 final int blockRow = i / nRowsInBlock;
87 final int rowInBlock = i - blockRow * nRowsInBlock;
88 final int blockOffset = rowInBlock * nColsInBlock;
89 for (int j = 0; j < width; j++) {
90 final int value = s[i * width + j];
91 final int blockCol = j / nColsInBlock;
92 final int colInBlock = j - blockCol * nColsInBlock;
93 final int index = blockOffset + colInBlock;
94 final int offset = index * bytesPerPixel;
95 final byte[] b = blocks[blockRow * nColsOfBlocks + blockCol];
96 if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
97 b[offset] = (byte) (value & 0xff);
98 b[offset + 1] = (byte) (value >> 8 & 0xff);
99 b[offset + 2] = (byte) (value >> 16 & 0xff);
100 b[offset + 3] = (byte) (value >> 24 & 0xff);
101 } else {
102 b[offset] = (byte) (value >> 24 & 0xff);
103 b[offset + 1] = (byte) (value >> 16 & 0xff);
104 b[offset + 2] = (byte) (value >> 8 & 0xff);
105 b[offset + 3] = (byte) (value & 0xff);
106 }
107 }
108 }
109
110 return blocks;
111 }
112
113 @Test
114 public void test() throws Exception {
115 final File[] testFile = new File[4];
116 testFile[0] = writeFile(32, ByteOrder.LITTLE_ENDIAN, false);
117 testFile[1] = writeFile(32, ByteOrder.BIG_ENDIAN, false);
118 testFile[2] = writeFile(32, ByteOrder.LITTLE_ENDIAN, true);
119 testFile[3] = writeFile(32, ByteOrder.BIG_ENDIAN, true);
120 for (int i = 0; i < testFile.length; i++) {
121 final String name = testFile[i].getName();
122 final ByteSource byteSource = ByteSource.file(testFile[i]);
123 final TiffReader tiffReader = new TiffReader(true);
124 final TiffContents contents = tiffReader.readDirectories(byteSource, true,
125 FormatCompliance.getDefault());
126 final TiffDirectory directory = contents.directories.get(0);
127 final TiffRasterData rdInt = directory.getRasterData(null);
128 final int[] test = rdInt.getIntData();
129 for (int j = 0; j < sample.length; j++) {
130 assertEquals(sample[j], test[j], "Extracted data does not match original, test " + name + ": " + i + ", index " + j);
131 }
132 final TiffImagingParameters params = new TiffImagingParameters();
133 params.setSubImage(2, 2, width - 4, height - 4);
134 final TiffRasterData rdSub = directory.getRasterData(params);
135 assertEquals(width - 4, rdSub.getWidth(), "Invalid sub-image width");
136 assertEquals(height - 4, rdSub.getHeight(), "Invalid sub-image height");
137 for (int x = 2; x < width - 2; x++) {
138 for (int y = 2; y < height - 2; y++) {
139 final int a = rdInt.getIntValue(x, y);
140 final int b = rdSub.getIntValue(x - 2, y - 2);
141 assertEquals(a, b, "Sub Image test failed at (" + x + "," + y + ")");
142 }
143 }
144 final TiffImagingParameters xparams = new TiffImagingParameters();
145 xparams.setSubImage(2, 2, width, height);
146 assertThrows(ImagingException.class, () -> directory.getRasterData(xparams), "Failed to catch bad subimage for test " + name);
147 }
148 }
149
150 private File writeFile(final int bitsPerSample, final ByteOrder byteOrder, final boolean useTiles) throws IOException, ImagingException {
151 final String name = String.format("Int32RoundTrip_%2d_%s_%s.tiff", bitsPerSample, byteOrder == ByteOrder.LITTLE_ENDIAN ? "LE" : "BE",
152 useTiles ? "Tiles" : "Strips");
153 final File outputFile = new File(tempDir.toFile(), name);
154
155 final int bytesPerSample = bitsPerSample / 8;
156 int nRowsInBlock;
157 int nColsInBlock;
158 int nBytesInBlock;
159 if (useTiles) {
160
161
162
163 nRowsInBlock = 12;
164 nColsInBlock = 20;
165 } else {
166
167
168
169 nRowsInBlock = 2;
170 nColsInBlock = width;
171 }
172 nBytesInBlock = nRowsInBlock * nColsInBlock * bytesPerSample;
173
174 byte[][] blocks;
175 blocks = getBytesForOutput32(sample, width, height, nRowsInBlock, nColsInBlock, byteOrder);
176
177
178
179
180 final TiffOutputSet outputSet = new TiffOutputSet(byteOrder);
181 final TiffOutputDirectory outDir = outputSet.addRootDirectory();
182 outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
183 outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
184 outDir.add(TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT, (short) TiffTagConstants.SAMPLE_FORMAT_VALUE_TWOS_COMPLEMENT_SIGNED_INTEGER);
185 outDir.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL, (short) 1);
186 outDir.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, (short) bitsPerSample);
187 outDir.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION, (short) TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_BLACK_IS_ZERO);
188 outDir.add(TiffTagConstants.TIFF_TAG_COMPRESSION, (short) TiffTagConstants.COMPRESSION_VALUE_UNCOMPRESSED);
189
190 outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION, (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY);
191
192 if (useTiles) {
193 outDir.add(TiffTagConstants.TIFF_TAG_TILE_WIDTH, nColsInBlock);
194 outDir.add(TiffTagConstants.TIFF_TAG_TILE_LENGTH, nRowsInBlock);
195 outDir.add(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS, nBytesInBlock);
196 } else {
197 outDir.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP, nRowsInBlock);
198 outDir.add(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS, nBytesInBlock);
199 }
200
201 final AbstractTiffElement.DataElement[] imageData = new AbstractTiffElement.DataElement[blocks.length];
202 for (int i = 0; i < blocks.length; i++) {
203 imageData[i] = new AbstractTiffImageData.Data(0, blocks[i].length, blocks[i]);
204 }
205
206 AbstractTiffImageData abstractTiffImageData;
207 if (useTiles) {
208 abstractTiffImageData = new AbstractTiffImageData.Tiles(imageData, nColsInBlock, nRowsInBlock);
209 } else {
210 abstractTiffImageData = new AbstractTiffImageData.Strips(imageData, nRowsInBlock);
211 }
212 outDir.setTiffImageData(abstractTiffImageData);
213
214 try (FileOutputStream fos = new FileOutputStream(outputFile);
215 BufferedOutputStream bos = new BufferedOutputStream(fos)) {
216 final TiffImageWriterLossy writer = new TiffImageWriterLossy(byteOrder);
217 writer.write(bos, outputSet);
218 bos.flush();
219 }
220 return outputFile;
221 }
222
223 }