1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.tiff.datareaders;
18
19 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_JPEG;
20
21 import java.awt.Rectangle;
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.nio.ByteOrder;
25
26 import org.apache.commons.imaging.ImagingException;
27 import org.apache.commons.imaging.common.Allocator;
28 import org.apache.commons.imaging.common.ImageBuilder;
29 import org.apache.commons.imaging.formats.tiff.AbstractTiffImageData;
30 import org.apache.commons.imaging.formats.tiff.TiffDirectory;
31 import org.apache.commons.imaging.formats.tiff.TiffRasterData;
32 import org.apache.commons.imaging.formats.tiff.TiffRasterDataFloat;
33 import org.apache.commons.imaging.formats.tiff.TiffRasterDataInt;
34 import org.apache.commons.imaging.formats.tiff.constants.TiffPlanarConfiguration;
35 import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
36 import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreter;
37 import org.apache.commons.imaging.formats.tiff.photometricinterpreters.PhotometricInterpreterRgb;
38
39
40
41
42
43
44 public final class DataReaderStrips extends ImageDataReader {
45
46 private final int bitsPerPixel;
47 private final int compression;
48 private final int rowsPerStrip;
49 private final TiffPlanarConfiguration planarConfiguration;
50 private final ByteOrder byteOrder;
51 private int x;
52 private int y;
53 private final AbstractTiffImageData.Strips imageData;
54
55 public DataReaderStrips(final TiffDirectory directory, final PhotometricInterpreter photometricInterpreter, final int bitsPerPixel,
56 final int[] bitsPerSample, final int predictor, final int samplesPerPixel, final int sampleFormat, final int width, final int height,
57 final int compression, final TiffPlanarConfiguration planarConfiguration, final ByteOrder byteOrder, final int rowsPerStrip,
58 final AbstractTiffImageData.Strips imageData) {
59 super(directory, photometricInterpreter, bitsPerSample, predictor, samplesPerPixel, sampleFormat, width, height, planarConfiguration);
60
61 this.bitsPerPixel = bitsPerPixel;
62 this.compression = compression;
63 this.rowsPerStrip = rowsPerStrip;
64 this.planarConfiguration = planarConfiguration;
65 this.imageData = imageData;
66 this.byteOrder = byteOrder;
67 }
68
69 private void interpretStrip(final ImageBuilder imageBuilder, final byte[] bytes, final int pixelsPerStrip, final int yLimit)
70 throws ImagingException, IOException {
71 if (y >= yLimit) {
72 return;
73 }
74
75
76 if (sampleFormat == TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT) {
77 int k = 0;
78 int nRows = pixelsPerStrip / width;
79 if (y + nRows > yLimit) {
80 nRows = yLimit - y;
81 }
82 final int i0 = y;
83 final int i1 = y + nRows;
84 x = 0;
85 y += nRows;
86 final int[] samples = new int[1];
87 final int[] b = unpackFloatingPointSamples(width, i1 - i0, width, bytes, bitsPerPixel, byteOrder);
88
89 for (int i = i0; i < i1; i++) {
90 for (int j = 0; j < width; j++) {
91 samples[0] = b[k];
92 k += samplesPerPixel;
93 photometricInterpreter.interpretPixel(imageBuilder, samples, j, i);
94 }
95 }
96
97 return;
98 }
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 final boolean allSamplesAreOneByte = isHomogenous(8);
142
143 if (predictor != 2 && bitsPerPixel == 8 && allSamplesAreOneByte) {
144 int k = 0;
145 int nRows = pixelsPerStrip / width;
146 if (y + nRows > yLimit) {
147 nRows = yLimit - y;
148 }
149 final int i0 = y;
150 final int i1 = y + nRows;
151 x = 0;
152 y += nRows;
153 final int[] samples = new int[1];
154 for (int i = i0; i < i1; i++) {
155 for (int j = 0; j < width; j++) {
156 samples[0] = bytes[k++] & 0xff;
157 photometricInterpreter.interpretPixel(imageBuilder, samples, j, i);
158 }
159 }
160 return;
161 }
162 if ((bitsPerPixel == 24 || bitsPerPixel == 32) && allSamplesAreOneByte && photometricInterpreter instanceof PhotometricInterpreterRgb) {
163 int k = 0;
164 int nRows = pixelsPerStrip / width;
165 if (y + nRows > yLimit) {
166 nRows = yLimit - y;
167 }
168 final int i0 = y;
169 final int i1 = y + nRows;
170 x = 0;
171 y += nRows;
172 if (predictor == TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING) {
173 applyPredictorToBlock(width, nRows, samplesPerPixel, bytes);
174 }
175
176 if (bitsPerPixel == 24) {
177
178
179 for (int i = i0; i < i1; i++) {
180 for (int j = 0; j < width; j++, k += 3) {
181 final int rgb = 0xff000000 | bytes[k] << 16 | (bytes[k + 1] & 0xff) << 8 | bytes[k + 2] & 0xff;
182 imageBuilder.setRgb(j, i, rgb);
183 }
184 }
185 } else {
186
187
188 for (int i = i0; i < i1; i++) {
189 for (int j = 0; j < width; j++, k += 4) {
190 final int rgb = (bytes[k] & 0xff) << 16 | (bytes[k + 1] & 0xff) << 8 | bytes[k + 2] & 0xff | bytes[k + 3] << 24;
191 imageBuilder.setRgb(j, i, rgb);
192 }
193 }
194 }
195
196 return;
197 }
198
199
200
201
202 try (BitInputStream bis = new BitInputStream(new ByteArrayInputStream(bytes), byteOrder)) {
203
204 int[] samples = Allocator.intArray(bitsPerSampleLength);
205 resetPredictor();
206 for (int i = 0; i < pixelsPerStrip; i++) {
207 getSamplesAsBytes(bis, samples);
208
209 if (x < width) {
210 samples = applyPredictor(samples);
211
212 photometricInterpreter.interpretPixel(imageBuilder, samples, x, y);
213 }
214
215 x++;
216 if (x >= width) {
217 x = 0;
218 resetPredictor();
219 y++;
220 bis.flushCache();
221 if (y >= yLimit) {
222 break;
223 }
224 }
225 }
226 }
227 }
228
229 @Override
230 public ImageBuilder readImageData(final Rectangle subImageSpecification, final boolean hasAlpha, final boolean isAlphaPreMultiplied)
231 throws IOException, ImagingException {
232
233 final Rectangle subImage;
234 if (subImageSpecification == null) {
235
236 subImage = new Rectangle(0, 0, width, height);
237 } else {
238 subImage = subImageSpecification;
239 }
240
241
242
243
244
245
246
247
248 final int strip0 = subImage.y / rowsPerStrip;
249 final int strip1 = (subImage.y + subImage.height - 1) / rowsPerStrip;
250 final int workingHeight = (strip1 - strip0 + 1) * rowsPerStrip;
251
252
253
254
255
256
257 final int y0 = strip0 * rowsPerStrip;
258 final int yLimit = subImage.y - y0 + subImage.height;
259
260
261
262
263
264
265
266
267 final ImageBuilder workingBuilder = new ImageBuilder(width, workingHeight, hasAlpha, isAlphaPreMultiplied);
268
269
270
271 final boolean interleaved = planarConfiguration != TiffPlanarConfiguration.PLANAR;
272 if (interleaved) {
273
274
275
276 for (int strip = strip0; strip <= strip1; strip++) {
277 final long rowsPerStripLong = 0xFFFFffffL & rowsPerStrip;
278 final long rowsRemaining = height - strip * rowsPerStripLong;
279 final long rowsInThisStrip = Math.min(rowsRemaining, rowsPerStripLong);
280 final long bytesPerRow = (bitsPerPixel * width + 7) / 8;
281 final long bytesPerStrip = rowsInThisStrip * bytesPerRow;
282 final long pixelsPerStrip = rowsInThisStrip * width;
283
284 final byte[] compressed = imageData.getImageData(strip).getData();
285
286 if (compression == TIFF_COMPRESSION_JPEG) {
287 final int yBlock = strip * rowsPerStrip;
288 final int yWork = yBlock - y0;
289 DataInterpreterJpeg.intepretBlock(directory, workingBuilder, 0, yWork, width, (int) rowsInThisStrip, compressed);
290 continue;
291 }
292
293 final byte[] decompressed = decompress(compressed, compression, (int) bytesPerStrip, width, (int) rowsInThisStrip);
294
295 interpretStrip(workingBuilder, decompressed, (int) pixelsPerStrip, yLimit);
296 }
297 } else {
298
299
300
301
302 if (compression == TIFF_COMPRESSION_JPEG) {
303 throw new ImagingException("TIFF file in non-supported configuration: JPEG compression used in planar configuration.");
304 }
305 final int nStripsInPlane = imageData.getImageDataLength() / 3;
306 for (int strip = strip0; strip <= strip1; strip++) {
307 final long rowsPerStripLong = 0xFFFFffffL & rowsPerStrip;
308 final long rowsRemaining = height - strip * rowsPerStripLong;
309 final long rowsInThisStrip = Math.min(rowsRemaining, rowsPerStripLong);
310 final long bytesPerRow = (bitsPerPixel * width + 7) / 8;
311 final long bytesPerStrip = rowsInThisStrip * bytesPerRow;
312 final long pixelsPerStrip = rowsInThisStrip * width;
313
314 final byte[] b = Allocator.byteArray((int) bytesPerStrip);
315 for (int iPlane = 0; iPlane < 3; iPlane++) {
316 final int planeStrip = iPlane * nStripsInPlane + strip;
317 final byte[] compressed = imageData.getImageData(planeStrip).getData();
318 final byte[] decompressed = decompress(compressed, compression, (int) bytesPerStrip, width, (int) rowsInThisStrip);
319 int index = iPlane;
320 for (final byte element : decompressed) {
321 b[index] = element;
322 index += 3;
323 }
324 }
325 interpretStrip(workingBuilder, b, (int) pixelsPerStrip, height);
326 }
327 }
328
329 if (subImage.x == 0 && subImage.y == y0 && subImage.width == width && subImage.height == workingHeight) {
330
331
332 return workingBuilder;
333 }
334 return workingBuilder.getSubset(subImage.x, subImage.y - y0, subImage.width, subImage.height);
335 }
336
337 @Override
338 public TiffRasterData readRasterData(final Rectangle subImage) throws ImagingException, IOException {
339 switch (sampleFormat) {
340 case TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT:
341 return readRasterDataFloat(subImage);
342 case TiffTagConstants.SAMPLE_FORMAT_VALUE_TWOS_COMPLEMENT_SIGNED_INTEGER:
343 return readRasterDataInt(subImage);
344 default:
345 throw new ImagingException("Unsupported sample format, value=" + sampleFormat);
346 }
347 }
348
349 private TiffRasterData readRasterDataFloat(final Rectangle subImage) throws ImagingException, IOException {
350 int xRaster;
351 int yRaster;
352 int rasterWidth;
353 int rasterHeight;
354 if (subImage != null) {
355 xRaster = subImage.x;
356 yRaster = subImage.y;
357 rasterWidth = subImage.width;
358 rasterHeight = subImage.height;
359 } else {
360 xRaster = 0;
361 yRaster = 0;
362 rasterWidth = width;
363 rasterHeight = height;
364 }
365
366 final float[] rasterDataFloat = Allocator.floatArray(rasterWidth * rasterHeight * samplesPerPixel);
367
368
369
370
371
372
373
374
375 final int strip0 = yRaster / rowsPerStrip;
376 final int strip1 = (yRaster + rasterHeight - 1) / rowsPerStrip;
377
378 for (int strip = strip0; strip <= strip1; strip++) {
379 final int yStrip = strip * rowsPerStrip;
380 final int rowsRemaining = height - yStrip;
381 final int rowsInThisStrip = Math.min(rowsRemaining, rowsPerStrip);
382 final int bytesPerRow = (bitsPerPixel * width + 7) / 8;
383 final int bytesPerStrip = rowsInThisStrip * bytesPerRow;
384
385 final byte[] compressed = imageData.getImageData(strip).getData();
386 final byte[] decompressed = decompress(compressed, compression, bytesPerStrip, width, rowsInThisStrip);
387
388 final int[] blockData = unpackFloatingPointSamples(width, rowsInThisStrip, width, decompressed, bitsPerPixel, byteOrder);
389 transferBlockToRaster(0, yStrip, width, rowsInThisStrip, blockData, xRaster, yRaster, rasterWidth, rasterHeight, samplesPerPixel, rasterDataFloat);
390 }
391 return new TiffRasterDataFloat(rasterWidth, rasterHeight, samplesPerPixel, rasterDataFloat);
392 }
393
394 private TiffRasterData readRasterDataInt(final Rectangle subImage) throws ImagingException, IOException {
395 int xRaster;
396 int yRaster;
397 int rasterWidth;
398 int rasterHeight;
399 if (subImage != null) {
400 xRaster = subImage.x;
401 yRaster = subImage.y;
402 rasterWidth = subImage.width;
403 rasterHeight = subImage.height;
404 } else {
405 xRaster = 0;
406 yRaster = 0;
407 rasterWidth = width;
408 rasterHeight = height;
409 }
410
411 final int[] rasterDataInt = Allocator.intArray(rasterWidth * rasterHeight);
412
413
414
415
416
417
418
419
420 final int strip0 = yRaster / rowsPerStrip;
421 final int strip1 = (yRaster + rasterHeight - 1) / rowsPerStrip;
422
423 for (int strip = strip0; strip <= strip1; strip++) {
424 final int yStrip = strip * rowsPerStrip;
425 final int rowsRemaining = height - yStrip;
426 final int rowsInThisStrip = Math.min(rowsRemaining, rowsPerStrip);
427 final int bytesPerRow = (bitsPerPixel * width + 7) / 8;
428 final int bytesPerStrip = rowsInThisStrip * bytesPerRow;
429
430 final byte[] compressed = imageData.getImageData(strip).getData();
431 final byte[] decompressed = decompress(compressed, compression, bytesPerStrip, width, rowsInThisStrip);
432 final int[] blockData = unpackIntSamples(width, rowsInThisStrip, width, decompressed, predictor, bitsPerPixel, byteOrder);
433 transferBlockToRaster(0, yStrip, width, rowsInThisStrip, blockData, xRaster, yRaster, rasterWidth, rasterHeight, rasterDataInt);
434 }
435 return new TiffRasterDataInt(rasterWidth, rasterHeight, rasterDataInt);
436 }
437 }