@Override
    public ImageBuilder readImageData(final Rectangle subImageSpecification, final boolean hasAlpha, final boolean isAlphaPreMultiplied)
            throws IOException, ImagingException {

        final Rectangle subImage;
        if (subImageSpecification == null) {
            // configure subImage to read entire image
            subImage = new Rectangle(0, 0, width, height);
        } else {
            subImage = subImageSpecification;
        }

        // the legacy code is optimized to the reading of whole
        // strips (except for the last strip in the image, which can
        // be a partial). So create a working image with compatible
        // dimensions and read that. Later on, the working image
        // will be sub-imaged to the proper size.
        // strip0 and strip1 give the indices of the strips containing
        // the first and last rows of pixels in the subimage
        final int strip0 = subImage.y / rowsPerStrip;
        final int strip1 = (subImage.y + subImage.height - 1) / rowsPerStrip;
        final int workingHeight = (strip1 - strip0 + 1) * rowsPerStrip;

        // the legacy code uses a member element "y" to keep track
        // of the row index of the output image that is being processed
        // by interpretStrip. y is set to zero before the first
        // call to interpretStrip. y0 will be the index of the first row
        // in the full image (the source image) that will be processed.
        final int y0 = strip0 * rowsPerStrip;
        final int yLimit = subImage.y - y0 + subImage.height;

        // When processing a subimage, the workingBuilder height is set
        // to be an integral multiple of the rowsPerStrip and
        // the full width of the strips. So the working image may be larger than
        // the specified size of the subimage. If necessary, the subimage
        // is extracted from the workingBuilder at the end of this method.
        // This approach avoids the need for the interpretStrips method
        // to implement bounds checking for a subimage.
        final ImageBuilder workingBuilder = new ImageBuilder(width, workingHeight, hasAlpha, isAlphaPreMultiplied);

        // the following statement accounts for cases where planar configuration
        // is not specified and the default (CHUNKY) is assumed.
        final boolean interleaved = planarConfiguration != TiffPlanarConfiguration.PLANAR;
        if (interleaved) {
            // Pixel definitions are organized in an interleaved format
            // For example, red-green-blue values for each pixel
            // would appear contiguous in input sequence.
            for (int strip = strip0; strip <= strip1; strip++) {
                final long rowsPerStripLong = 0xFFFFffffL & rowsPerStrip;
                final long rowsRemaining = height - strip * rowsPerStripLong;
                final long rowsInThisStrip = Math.min(rowsRemaining, rowsPerStripLong);
                final long bytesPerRow = (bitsPerPixel * width + 7) / 8;
                final long bytesPerStrip = rowsInThisStrip * bytesPerRow;
                final long pixelsPerStrip = rowsInThisStrip * width;

                final byte[] compressed = imageData.getImageData(strip).getData();

                if (compression == COMPRESSION_JPEG) {
                    final int yBlock = strip * rowsPerStrip;
                    final int yWork = yBlock - y0;
                    DataInterpreterJpeg.intepretBlock(directory, workingBuilder, 0, yWork, width, (int) rowsInThisStrip, compressed);
                    continue;
                }

                final byte[] decompressed = decompress(compressed, compression, (int) bytesPerStrip, width, (int) rowsInThisStrip);

                interpretStrip(workingBuilder, decompressed, (int) pixelsPerStrip, yLimit);
            }
        } else {
            // pixel definitions are organized in a 3 separate sections of input
            // sequence. For example, red-green-blue values would be given as
            // red values for all pixels, followed by green values for all pixels,
            // etc.
            if (compression == COMPRESSION_JPEG) {
                throw new ImagingException("TIFF file in non-supported configuration: JPEG compression used in planar configuration.");
            }
            final int nStripsInPlane = imageData.getImageDataLength() / 3;
            for (int strip = strip0; strip <= strip1; strip++) {
                final long rowsPerStripLong = 0xFFFFffffL & rowsPerStrip;
                final long rowsRemaining = height - strip * rowsPerStripLong;
                final long rowsInThisStrip = Math.min(rowsRemaining, rowsPerStripLong);
                final long bytesPerRow = (bitsPerPixel * width + 7) / 8;
                final long bytesPerStrip = rowsInThisStrip * bytesPerRow;
                final long pixelsPerStrip = rowsInThisStrip * width;

                final byte[] b = Allocator.byteArray((int) bytesPerStrip);
                for (int iPlane = 0; iPlane < 3; iPlane++) {
                    final int planeStrip = iPlane * nStripsInPlane + strip;
                    final byte[] compressed = imageData.getImageData(planeStrip).getData();
                    final byte[] decompressed = decompress(compressed, compression, (int) bytesPerStrip, width, (int) rowsInThisStrip);
                    int index = iPlane;
                    for (final byte element : decompressed) {
                        b[index] = element;
                        index += 3;
                    }
                }
                interpretStrip(workingBuilder, b, (int) pixelsPerStrip, height);
            }
        }

        if (subImage.x == 0 && subImage.y == y0 && subImage.width == width && subImage.height == workingHeight) {
            // the subimage exactly matches the ImageBuilder bounds
            // so we can return that.
            return workingBuilder;
        }
        return workingBuilder.getSubset(subImage.x, subImage.y - y0, subImage.width, subImage.height);
    }
Provides a data reader for TIFF file images organized by tiles.
<p>
See {@link AbstractImageDataReader} for notes discussing design and development with particular emphasis on run-time performance.
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java