private void interpretTile(final ImageBuilder imageBuilder, final byte[] bytes, final int startX, final int startY, final int xLimit, final int yLimit)
            throws ImagingException, IOException {

        // March 2020 change to handle floating-point with compression
        // for the compressed floating-point, there is a standard that allows
        // 16 bit floats (which is an IEEE 754 standard) and 24 bits (which is
        // a non-standard format implemented for TIFF). At this time, this
        // code only supports the 32-bit and 64-bit formats.
        if (sampleFormat == TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT) {
            // tileLength: number of rows in tile
            // tileWidth: number of columns in tile
            final int i0 = startY;
            int i1 = startY + tileLength;
            if (i1 > yLimit) {
                // the tile is padded past bottom of image
                i1 = yLimit;
            }
            final int j0 = startX;
            int j1 = startX + tileWidth;
            if (j1 > xLimit) {
                // the tile is padded to beyond the tile width
                j1 = xLimit;
            }
            final int[] samples = new int[4];
            final int[] b = unpackFloatingPointSamples(j1 - j0, i1 - i0, tileWidth, bytes, bitsPerPixel, byteOrder);
            for (int i = i0; i < i1; i++) {
                final int row = i - startY;
                final int rowOffset = row * tileWidth;
                for (int j = j0; j < j1; j++) {
                    final int column = j - startX;
                    final int k = (rowOffset + column) * samplesPerPixel;
                    samples[0] = b[k];
                    photometricInterpreter.interpretPixel(imageBuilder, samples, j, i);
                }
            }
            return;
        }

        // End of March 2020 changes to support floating-point format
        // changes introduced May 2012
        // The following block of code implements changes that
        // reduce image loading time by using special-case processing
        // instead of the general-purpose logic from the original
        // implementation. For a detailed discussion, see the comments for
        // a similar treatment in the DataReaderStrip class
        //
        // verify that all samples are one byte in size
        final boolean allSamplesAreOneByte = isHomogenous(8);

        if ((bitsPerPixel == 24 || bitsPerPixel == 32) && allSamplesAreOneByte && photometricInterpreter instanceof PhotometricInterpreterRgb) {
            int i1 = startY + tileLength;
            if (i1 > yLimit) {
                // the tile is padded past bottom of image
                i1 = yLimit;
            }
            int j1 = startX + tileWidth;
            if (j1 > xLimit) {
                // the tile is padded to beyond the tile width
                j1 = xLimit;
            }

            if (predictor == TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING) {
                applyPredictorToBlock(tileWidth, i1 - startY, 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 = startY; i < i1; i++) {
                    int k = (i - startY) * tileWidth * 3;
                    for (int j = startX; j < j1; 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 if (bitsPerPixel == 32) {
                // 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 = startY; i < i1; i++) {
                    int k = (i - startY) * tileWidth * 4;
                    for (int j = startX; j < j1; 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;
        }

        // End of May 2012 changes
        try (BitInputStream bis = new BitInputStream(new ByteArrayInputStream(bytes), byteOrder)) {

            final int pixelsPerTile = tileWidth * tileLength;

            int tileX = 0;
            int tileY = 0;

            int[] samples = Allocator.intArray(bitsPerSampleLength);
            resetPredictor();
            for (int i = 0; i < pixelsPerTile; i++) {

                final int x = tileX + startX;
                final int y = tileY + startY;

                getSamplesAsBytes(bis, samples);

                if (x < xLimit && y < yLimit) {
                    samples = applyPredictor(samples);
                    photometricInterpreter.interpretPixel(imageBuilder, samples, x, y);
                }

                tileX++;

                if (tileX >= tileWidth) {
                    tileX = 0;
                    resetPredictor();
                    tileY++;
                    bis.flushCache();
                    if (tileY >= tileLength) {
                        break;
                    }
                }

            }
        }
    }
Provides a data reader for TIFF file images organized by tiles.
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderTiled.java