in src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java [73:245]
private void interpretStrip(
final ImageBuilder imageBuilder,
final byte[] bytes,
final int pixelsPerStrip,
final int yLimit) throws ImagingException, IOException {
if (y >= yLimit) {
return;
}
// changes added March 2020
if (sampleFormat == TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT) {
int k = 0;
int nRows = pixelsPerStrip / width;
if (y + nRows > yLimit) {
nRows = yLimit - y;
}
final int i0 = y;
final int i1 = y + nRows;
x = 0;
y += nRows;
final int[] samples = new int[1];
final int[] b = unpackFloatingPointSamples(
width, i1 - i0, width, bytes, bitsPerPixel, byteOrder);
for (int i = i0; i < i1; i++) {
for (int j = 0; j < width; j++) {
samples[0] = b[k];
k += samplesPerPixel;
photometricInterpreter.interpretPixel(imageBuilder,
samples, j, i);
}
}
return;
}
// changes added May 2012
// In the original implementation, a general-case bit reader called
// getSamplesAsBytes is used to retrieve the samples (raw data values)
// for each pixel in the strip. These samples are then passed into a
// photogrammetric interpreter that converts them to ARGB pixel values
// and stores them in the image. Because the bit-reader must handle
// a large number of formats, it involves several conditional
// branches that must be executed each time a pixel is read.
// Depending on the size of an image, the same evaluations must be
// executed redundantly thousands and perhaps millions of times
// in order to process the complete collection of pixels.
// This code attempts to remove that redundancy by
// evaluating the format up-front and bypassing the general-format
// code for two commonly used data formats: the 8 bits-per-pixel
// and 24 bits-per-pixel cases. For these formats, the
// special case code achieves substantial reductions in image-loading
// time. In other cases, it simply falls through to the original code
// and continues to read the data correctly as it did in previous
// versions of this class.
// In addition to bypassing the getBytesForSample() method,
// the 24-bit case also implements a special block for RGB
// formatted images. To get a sense of the contributions of each
// optimization (removing getSamplesAsBytes and removing the
// photometric interpreter), consider the following results from tests
// conducted with large TIFF images using the 24-bit RGB format
// bypass getSamplesAsBytes: 67.5 % reduction
// bypass both optimizations: 77.2 % reduction
//
//
// Future Changes
// Both of the 8-bit and 24-bit blocks make the assumption that a strip
// always begins on x = 0 and that each strip exactly fills out the rows
// it contains (no half rows). The original code did not make this
// assumption, but the approach is consistent with the TIFF 6.0 spec
// (1992),
// and should probably be considered as an enhancement to the
// original general-case code block that remains from the original
// implementation. Taking this approach saves one conditional
// operation per pixel or about 5 percent of the total run time
// in the 8 bits/pixel case.
// verify that all samples are one byte in size
final boolean allSamplesAreOneByte = isHomogenous(8);
if (predictor != 2 && bitsPerPixel == 8 && allSamplesAreOneByte) {
int k = 0;
int nRows = pixelsPerStrip / width;
if (y + nRows > yLimit) {
nRows = yLimit - y;
}
final int i0 = y;
final int i1 = y + nRows;
x = 0;
y += nRows;
final int[] samples = new int[1];
for (int i = i0; i < i1; i++) {
for (int j = 0; j < width; j++) {
samples[0] = bytes[k++] & 0xff;
photometricInterpreter.interpretPixel(imageBuilder,
samples, j, i);
}
}
return;
}
if ((bitsPerPixel == 24 || bitsPerPixel == 32) && allSamplesAreOneByte
&& photometricInterpreter instanceof PhotometricInterpreterRgb) {
int k = 0;
int nRows = pixelsPerStrip / width;
if (y + nRows > yLimit) {
nRows = yLimit - y;
}
final int i0 = y;
final int i1 = y + nRows;
x = 0;
y += nRows;
if (predictor == TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING) {
applyPredictorToBlock(width, nRows, samplesPerPixel, bytes);
}
if (bitsPerPixel == 24) {
// 24 bit case, we don't mask the red byte because any
// sign-extended bits get covered by opacity mask
for (int i = i0; i < i1; i++) {
for (int j = 0; j < width; j++, k += 3) {
final int rgb = 0xff000000
| (bytes[k] << 16)
| ((bytes[k + 1] & 0xff) << 8)
| (bytes[k + 2] & 0xff);
imageBuilder.setRgb(j, i, rgb);
}
}
} else {
// 32 bit case, we don't mask the high byte because any
// sign-extended bits get shifted up and out of result
for (int i = i0; i < i1; i++) {
for (int j = 0; j < width; j++, k += 4) {
final int rgb
= ((bytes[k] & 0xff) << 16)
| ((bytes[k + 1] & 0xff) << 8)
| (bytes[k + 2] & 0xff)
| (bytes[k + 3] << 24);
imageBuilder.setRgb(j, i, rgb);
}
}
}
return;
}
// original code before May 2012 modification
// this logic will handle all cases not conforming to the
// special case handled above
try (BitInputStream bis = new BitInputStream(new ByteArrayInputStream(bytes), byteOrder)) {
int[] samples = Allocator.intArray(bitsPerSampleLength);
resetPredictor();
for (int i = 0; i < pixelsPerStrip; i++) {
getSamplesAsBytes(bis, samples);
if (x < width) {
samples = applyPredictor(samples);
photometricInterpreter.interpretPixel(imageBuilder, samples, x, y);
}
x++;
if (x >= width) {
x = 0;
resetPredictor();
y++;
bis.flushCache();
if (y >= yLimit) {
break;
}
}
}
}
}