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 TiffShortIntRoundTripTest extends TiffBaseTest {
45
46 @TempDir
47 Path tempDir;
48
49 int width = 48;
50 int height = 23;
51
52 short[] sample = new short[width * height];
53
54 public TiffShortIntRoundTripTest() {
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] = (short) (index - 10);
60 }
61 }
62 }
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77 private byte[][] getBytesForOutput16(final short[] 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 = 2;
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 } else {
100 b[offset] = (byte) (value >> 8 & 0xff);
101 b[offset + 1] = (byte) (value & 0xff);
102 }
103 }
104 }
105
106 return blocks;
107 }
108
109 @Test
110 public void test() throws Exception {
111 final File[] testFile = new File[4];
112 testFile[0] = writeFile(16, ByteOrder.LITTLE_ENDIAN, false);
113 testFile[1] = writeFile(16, ByteOrder.BIG_ENDIAN, false);
114 testFile[2] = writeFile(16, ByteOrder.LITTLE_ENDIAN, true);
115 testFile[3] = writeFile(16, ByteOrder.BIG_ENDIAN, true);
116 for (int i = 0; i < testFile.length; i++) {
117 final String name = testFile[i].getName();
118 final ByteSource byteSource = ByteSource.file(testFile[i]);
119 final TiffReader tiffReader = new TiffReader(true);
120 final TiffContents contents = tiffReader.readDirectories(byteSource, true,
121 FormatCompliance.getDefault());
122 final TiffDirectory directory = contents.directories.get(0);
123 final TiffRasterData rdInt = directory.getRasterData(null);
124 final int[] test = rdInt.getIntData();
125 for (int j = 0; j < sample.length; j++) {
126 assertEquals(sample[j], test[j], "Extracted data does not match original, test " + name + ": " + i + ", index " + j);
127 }
128 final TiffImagingParameters params = new TiffImagingParameters();
129 params.setSubImage(2, 2, width - 4, height - 4);
130 final TiffRasterData rdSub = directory.getRasterData(params);
131 assertEquals(width - 4, rdSub.getWidth(), "Invalid sub-image width");
132 assertEquals(height - 4, rdSub.getHeight(), "Invalid sub-image height");
133 for (int x = 2; x < width - 2; x++) {
134 for (int y = 2; y < height - 2; y++) {
135 final int a = rdInt.getIntValue(x, y);
136 final int b = rdSub.getIntValue(x - 2, y - 2);
137 assertEquals(a, b, "Sub Image test failed at (" + x + "," + y + ")");
138 }
139 }
140 final TiffImagingParameters xparams = new TiffImagingParameters();
141 xparams.setSubImage(2, 2, width, height);
142 assertThrows(ImagingException.class, () -> directory.getRasterData(xparams), "Failed to catch bad subimage for test " + name);
143 }
144 }
145
146 private File writeFile(final int bitsPerSample, final ByteOrder byteOrder, final boolean useTiles) throws IOException, ImagingException {
147 final String name = String.format("ShortIntRoundTrip_%2d_%s_%s.tiff", bitsPerSample, byteOrder == ByteOrder.LITTLE_ENDIAN ? "LE" : "BE",
148 useTiles ? "Tiles" : "Strips");
149 final File outputFile = new File(tempDir.toFile(), name);
150
151 final int bytesPerSample = bitsPerSample / 8;
152 int nRowsInBlock;
153 int nColsInBlock;
154 int nBytesInBlock;
155 if (useTiles) {
156
157
158
159 nRowsInBlock = 12;
160 nColsInBlock = 20;
161 } else {
162
163
164
165 nRowsInBlock = 2;
166 nColsInBlock = width;
167 }
168 nBytesInBlock = nRowsInBlock * nColsInBlock * bytesPerSample;
169
170 byte[][] blocks;
171 blocks = getBytesForOutput16(sample, width, height, nRowsInBlock, nColsInBlock, byteOrder);
172
173
174
175
176 final TiffOutputSet outputSet = new TiffOutputSet(byteOrder);
177 final TiffOutputDirectory outDir = outputSet.addRootDirectory();
178 outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
179 outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
180 outDir.add(TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT, (short) TiffTagConstants.SAMPLE_FORMAT_VALUE_TWOS_COMPLEMENT_SIGNED_INTEGER);
181 outDir.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL, (short) 1);
182 outDir.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, (short) bitsPerSample);
183 outDir.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION, (short) TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_BLACK_IS_ZERO);
184 outDir.add(TiffTagConstants.TIFF_TAG_COMPRESSION, (short) TiffTagConstants.COMPRESSION_VALUE_UNCOMPRESSED);
185
186 outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION, (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY);
187
188 if (useTiles) {
189 outDir.add(TiffTagConstants.TIFF_TAG_TILE_WIDTH, nColsInBlock);
190 outDir.add(TiffTagConstants.TIFF_TAG_TILE_LENGTH, nRowsInBlock);
191 outDir.add(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS, nBytesInBlock);
192 } else {
193 outDir.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP, nRowsInBlock);
194 outDir.add(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS, nBytesInBlock);
195 }
196
197 final AbstractTiffElement.DataElement[] imageData = new AbstractTiffElement.DataElement[blocks.length];
198 for (int i = 0; i < blocks.length; i++) {
199 imageData[i] = new AbstractTiffImageData.Data(0, blocks[i].length, blocks[i]);
200 }
201
202 AbstractTiffImageData abstractTiffImageData;
203 if (useTiles) {
204 abstractTiffImageData = new AbstractTiffImageData.Tiles(imageData, nColsInBlock, nRowsInBlock);
205 } else {
206 abstractTiffImageData = new AbstractTiffImageData.Strips(imageData, nRowsInBlock);
207 }
208 outDir.setTiffImageData(abstractTiffImageData);
209
210 try (FileOutputStream fos = new FileOutputStream(outputFile);
211 BufferedOutputStream bos = new BufferedOutputStream(fos)) {
212 final TiffImageWriterLossy writer = new TiffImageWriterLossy(byteOrder);
213 writer.write(bos, outputSet);
214 bos.flush();
215 }
216 return outputFile;
217 }
218
219 }