gwlucastrig commented on a change in pull request #173:
URL: https://github.com/apache/commons-imaging/pull/173#discussion_r730327762



##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/TiffRasterData.java
##########
@@ -23,37 +23,62 @@
  * <p>
  * <strong>Note:</strong> The getData() and getIntData() methods can return
  * direct references to the internal arrays stored in instances of this class.
- * Because these are not safe copies of the data, an application that
- * modified the arrays returned by these methods will change the content
- * of the associated instance. This approach is used for purposes of efficiency
- * when dealing with very large TIFF images.
+ * Because these are not safe copies of the data, an application that modified
+ * the arrays returned by these methods will change the content of the
+ * associated instance. This approach is used for purposes of efficiency when
+ * dealing with very large TIFF images.
+ * <p>
+ * <strong>Data layout:</strong> The elements in the returned array are stored
+ * in row-major order. In cases where the data contains multiple samples per
+ * raster cell (pixel), the data is organized into blocks of data one sample at
+ * a time. The first block contains width*height values for the first sample 
for
+ * each cell, the second block contains width*height values for the second
+ * sample for each cell, etc. Thus, the array index for a particular value is
+ * computed as
+ * <pre>
+ *    index = y*width + x + iSample * width *height;
+ * </pre>
  */
 public abstract class TiffRasterData {
 
     protected final int width;
     protected final int height;
+    protected final int samplesPerPixel;
+    protected final int nCells;
+    protected final int planarOffset;
+
 
     /**
      * Construct an instance allocating memory for the specified dimensions.
      *
      * @param width a value of 1 or greater
      * @param height a value of 1 or greater
+     * @param samplesPerPixel a value of 1 or greater
      */
-    public TiffRasterData(final int width, final int height) {
-        if (width <= 0 || height <= 0) {
+    public TiffRasterData(final int width, final int height, int 
samplesPerPixel) {
+        if (width <= 0 || height <= 0 || samplesPerPixel <= 0) {
             throw new IllegalArgumentException(
                     "Raster dimensions less than or equal to zero are not 
supported");

Review comment:
       Broke samples-per-pixel into it's own exception and provided a message 
that gave a bit more detail.

##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/TiffRasterDataFloat.java
##########
@@ -113,10 +182,26 @@ public float getValue(final int x, final int y) {
      */
     @Override
     public void setIntValue(final int x, final int y, final int value) {
-        int index = checkCoordinatesAndComputeIndex(x, y);
+        int index = checkCoordinatesAndComputeIndex(x, y, 0);
+        data[index] = value;
+    }
+
+    /**
+     * Sets the value stored at the specified raster coordinates.
+     *
+     * @param x integer coordinate in the columnar direction
+     * @param y integer coordinate in the row direction
+     * @param i integer sample index (for data sets giving multiple samples per
+     * raster cell).
+     * @param value the value to be stored at the specified location
+     */
+    @Override
+    public void setIntValue(final int x, final int y, int i, final int value) {
+        int index = checkCoordinatesAndComputeIndex(x, y, 0);
         data[index] = value;
     }
 
+

Review comment:
       está feito

##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/DataReaderStrips.java
##########
@@ -412,11 +401,11 @@ private TiffRasterData readRasterDataFloat(final 
Rectangle subImage)
                     rowsInThisStrip,
                     width,
                     decompressed,
-                    predictor, bitsPerPixel, byteOrder);
+                    bitsPerPixel, byteOrder);
             transferBlockToRaster(0, yStrip, width, (int) rowsInThisStrip, 
blockData,
-                    xRaster, yRaster, rasterWidth, rasterHeight, 
rasterDataFloat);
+                    xRaster, yRaster, rasterWidth, rasterHeight, 
samplesPerPixel, rasterDataFloat);
         }
-        return new TiffRasterDataFloat(rasterWidth, rasterHeight, 
rasterDataFloat);
+        return new TiffRasterDataFloat(rasterWidth, rasterHeight, 
samplesPerPixel, rasterDataFloat);

Review comment:
       de nada

##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/ImageDataReader.java
##########
@@ -62,15 +63,15 @@
  * Unfortunately, the TIFF floating-point format allows for a lot of different
  * variations. At this time, only the most widely used of these are supported.
  * When this code was written, only a small set of test data products were
- * available. Thus it is likely that developers will wish to extend the
- * range of floating-point data that can be processed as
- * additional test data become available. When implementing extensions to this
- * logic, developers are reminded that image processing requires
- * the handling of literally millions of pixels, so attention to performance
- * is essential to a successful implementation (please see the notes in
- * DataReaderStrips.java for more information).
+ * available. Thus it is likely that developers will wish to extend the range 
of
+ * floating-point data that can be processed as additional test data become
+ * available. When implementing extensions to this logic, developers are
+ * reminded that image processing requires the handling of literally millions 
of
+ * pixels, so attention to performance is essential to a successful
+ * implementation (please see the notes in DataReaderStrips.java for more
+ * information).
  * <p>
- * The TIFF floating-point specification is very poorly documented. So these
+ * The TIFF floating-point specification is poorly documented. So these

Review comment:
       I've been trying to rein in my habit of overstatement.

##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/datareaders/ImageDataReader.java
##########
@@ -126,23 +125,52 @@
  *     32 bits    IEEE-754 single-precision standard
  *     16 bits    IEEE-754 half-precision standard
  *     24 bits    A non-standard representation
- * </pre>
- * At this time, we have not obtained data samples for the smaller
+ * </pre> At this time, we have not obtained data samples for the smaller
  * representations used in combination with a predictor.
  * <p>
  * <strong>Interleaved formats</strong>
  * <p>
- * TIFF Technical Note 3 also provides sample code for interleaved data, such 
as
- * a real-valued vector or a complex pair. At this time no samples of
- * interleaved data were available. As a caveat, the specification that the
- * document provides has disadvantages in terms of code complexity and
- * performance. Because the interleaved evaluation is embedded inside the pixel
- * row and column loops, it puts a lot of redundant conditional evaluations
- * inside the double nested loops. It is recommended that when interleaved data
- * is implemented, it should get their own block of code so as not to interfere
- * with the processing of the more common non-interleaved variations.
+ * TIFF Technical Note 3 also provides example code for cases where each pixel
+ * (or raster cell) in the image is associated with more than one 
floating-point
+ * samples. Data in this format might be used for real-valued vector data,
+ * complex-valued pairs, or other numerical applications).
+ * <p>At this time, we have encountered only a limited selection of the 
possible
+ * configurations for multi-variable data. The code below only supports those
+ * configurations for which we had actual images that could be used to verify
+ * our implementation.  The implementation supports the following formats:
+ * <ul>
+ * <li>32-bit floating-point data</li>
+ * <li>Uncompressed, Deflate, or LZW compression</li>
+ * <li>Optional horizontal predictors used with compression</li>
+ * <li>PlanarConfiguration interleaved (CHUNKY) or non-interleaved (PLANAR)

Review comment:
       Done

##########
File path: 
src/test/java/org/apache/commons/imaging/formats/tiff/TiffRasterDataTest.java
##########
@@ -84,6 +85,45 @@ public void testGetValue() {
         }
     }
 
+    /**
+     * Test of setValue method, of class TiffRasterData.
+     */
+    @Test
+    public void testSetValue2() {
+        final TiffRasterData instance = new TiffRasterDataFloat(width, height, 
2);
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                final int index = y * width + height;
+                instance.setValue(x, y, 1, index);
+                final int test = (int) instance.getValue(x, y, 1);
+                assertEquals(index, test, "Set/get value test failed at (" + x 
+ "," + y + ")");
+                instance.setIntValue(x, y, 1, index);
+                final int iTest = instance.getIntValue(x, y, 1);
+                assertEquals(index, iTest, "Get/set value test failed at (" + 
x + "," + y + ")");
+            }
+        }
+    }
+
+    /**
+     * Test of getValue method, of class TiffRasterData.
+     */
+    @Test
+    public void testGetValue2() {
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++) {
+                final int index = y * width + x;
+                final int test = (int) raster.getValue(x, y, 0);
+                assertEquals(index, test, "Get into source data test failed at 
(" + x + "," + y + ")");
+                final int iTest = raster.getIntValue(x, y, 0);
+                assertEquals(index, iTest, "Get into source data test failed 
at (" + x + "," + y + ")");
+            }
+        }
+    }
+
+
+
+
+

Review comment:
       done

##########
File path: 
src/test/java/org/apache/commons/imaging/formats/tiff/TiffFloatingPointMultivariableTest.java
##########
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.imaging.formats.tiff;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.awt.image.BufferedImage;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.commons.imaging.FormatCompliance;
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.imaging.common.ImageBuilder;
+import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
+import org.apache.commons.imaging.formats.tiff.constants.TiffConstants;
+import 
org.apache.commons.imaging.formats.tiff.constants.TiffPlanarConfiguration;
+import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
+import 
org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PhotometricInterpreterFloat;
+import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy;
+import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
+import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Performs a test in which a TIFF file with the special-purpose floating-point
+ * sample type is used to store data to a file. The file is then read to see if
+ * it matches the original values.
+ * <p>
+ * At this time, Commons Imaging does not fully implement the floating-point
+ * specification. Currently, this class only tests the use of uncompressed
+ * floating point values in the Strips format. The Tiles format is not
+ * exercised.
+ */
+public class TiffFloatingPointMultivariableTest extends TiffBaseTest {
+
+    @TempDir
+    Path tempDir;
+
+    int width = 48;
+    int height = 23;
+    int samplesPerPixel = 2;
+    float f0 = 0.0F;
+    float f1 = 1.0F;
+    float[] fSample = new float[width * height * samplesPerPixel];
+    public TiffFloatingPointMultivariableTest() {
+        for(int iPlane = 0; iPlane<2; iPlane++){
+            int pOffset = iPlane*width*height;
+            for(int iRow=0; iRow<height; iRow++){
+                for(int iCol=0; iCol<width; iCol++){
+                    int index = pOffset + iRow*width +iCol;
+                    fSample[index] = index;
+                }
+            }
+        }
+    }
+
+
+
+    @Test
+    public void test() throws Exception {
+        // we set up the 32 and 64 bit test cases.  At this time,
+        // the Tile format is not supported for floating-point samples by the
+        // TIFF datareaders classes.  So that format is not yet exercised.
+        // Note also that the compressed floating-point with predictor=3
+        // is processed in other tests, but not here.
+        List<File>testFiles = new ArrayList<>();
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, false, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    false, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, true, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    true, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, false, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    false, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, true, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    true, false, 
TiffPlanarConfiguration.PLANAR));
+
+        // To exercise the horizontal-differencing-predictor logic, we include 
a writer that will
+        // reorganize the bytes into the form used by the floating-pont 
horizontal predictor.
+        // This test does not apply data compression, but it does apply the 
predictor.
+        // Note that although the TIFF predictor does not require big-endian 
formats, per se,
+        // the test logic implemented here does.
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN, true, true, 
TiffPlanarConfiguration.PLANAR));
+
+        for(File testFile : testFiles){
+            final String name = testFile.getName();
+            final ByteSourceFile byteSource = new ByteSourceFile(testFile);
+            final TiffReader tiffReader = new TiffReader(true);
+            final TiffContents contents = tiffReader.readDirectories(
+                byteSource,
+                true, // indicates that application should read image data, if 
present
+                FormatCompliance.getDefault());
+            final TiffDirectory directory = contents.directories.get(0);
+            final HashMap<String, Object> params = new HashMap<>();
+            final ByteOrder byteOrder = tiffReader.getByteOrder();
+            final TiffRasterData raster = directory.getRasterData(params);
+            assertNotNull(raster, "Failed to get raster from " + name);
+            assertEquals(2, raster.getSamplesPerPixel(), "Invalid samples per 
pixel in " + name);
+            for(int iPlane = 0; iPlane<2; iPlane++){
+                int pOffset = iPlane*width*height;
+                for(int iRow=0; iRow<height; iRow++){
+                    for(int iCol=0; iCol<width; iCol++){
+                        int index = pOffset + iRow*width +iCol;
+                        float tValue = fSample[index];
+                        float rValue = raster.getValue(iCol, iRow, iPlane);
+                        assertEquals(tValue, rValue, "Failed at index 
x="+iCol+", y="+iRow+", iPlane="+iPlane);
+                    }
+                }
+            }
+        }
+    }
+
+    private File writeFile(
+        final ByteOrder byteOrder,
+        final boolean useTiles,
+        final boolean usePredictorForTiles,
+        TiffPlanarConfiguration planarConfiguration ) throws IOException, 
ImageWriteException {
+
+        final String name = String.format("FpMultiVarRoundTrip_%s_%s%s.tiff",
+            planarConfiguration==TiffPlanarConfiguration.CHUNKY ? "Chunky" : 
"Planar",
+            useTiles ? "Tiles" : "Strips",
+            usePredictorForTiles ? "_Predictor" : "");
+        final File outputFile = new File(tempDir.toFile(), name);
+
+        final int bytesPerSample = 4 * samplesPerPixel;
+        final int bitsPerSample =  8 * bytesPerSample;
+
+        int nRowsInBlock;
+        int nColsInBlock;
+        int nBytesInBlock;
+        if (useTiles) {
+            // Define the tiles so that they will not evenly subdivide
+            // the image.  This will allow the test to evaluate how the
+            // data reader processes tiles that are only partially used.
+            nRowsInBlock = 12;
+            nColsInBlock = 20;
+        } else {
+            // Define the strips so that they will not evenly subdivide
+            // the image.  This will allow the test to evaluate how the
+            // data reader processes strips that are only partially used.
+            nRowsInBlock = 2;
+            nColsInBlock = width;
+        }
+        nBytesInBlock = nRowsInBlock * nColsInBlock * bytesPerSample;
+
+        byte[][] blocks;
+            blocks = this.getBytesForOutput32(nRowsInBlock, nColsInBlock, 
byteOrder,
+            useTiles, planarConfiguration);
+
+        final TiffOutputSet outputSet = new TiffOutputSet(byteOrder);
+        final TiffOutputDirectory outDir = outputSet.addRootDirectory();
+        outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
+        outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
+        outDir.add(TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT,
+            (short) TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT);
+        outDir.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL, (short) 
samplesPerPixel);
+        outDir.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, (short) 
bitsPerSample);
+        outDir.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION,
+            (short) 
TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_BLACK_IS_ZERO);
+        outDir.add(TiffTagConstants.TIFF_TAG_COMPRESSION,
+            (short) TiffTagConstants.COMPRESSION_VALUE_UNCOMPRESSED);
+
+        if(useTiles && usePredictorForTiles){
+            outDir.add(TiffTagConstants.TIFF_TAG_PREDICTOR,
+            (short) 
TiffTagConstants.PREDICTOR_VALUE_FLOATING_POINT_DIFFERENCING);
+              for(int iBlock=0; iBlock<blocks.length; iBlock++){
+                  applyTilePredictor(nRowsInBlock, nColsInBlock, 
blocks[iBlock]);
+              }
+        }
+
+        if(planarConfiguration==TiffPlanarConfiguration.CHUNKY){
+        outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION,
+            (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY);
+        }else{
+               outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION,
+            (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_PLANAR);
+        }
+
+        if (useTiles) {
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_WIDTH, nColsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_LENGTH, nRowsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS, 
nBytesInBlock);
+        } else {
+            outDir.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP, nRowsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS, 
nBytesInBlock);
+        }
+
+        final TiffElement.DataElement[] imageData = new 
TiffElement.DataElement[blocks.length];
+        for (int i = 0; i < blocks.length; i++) {
+            imageData[i] = new TiffImageData.Data(0, blocks[i].length, 
blocks[i]);
+        }
+
+        TiffImageData tiffImageData;
+        if (useTiles) {
+            tiffImageData
+                = new TiffImageData.Tiles(imageData, nColsInBlock, 
nRowsInBlock);
+        } else {
+            tiffImageData
+                = new TiffImageData.Strips(imageData, nRowsInBlock);
+        }
+        outDir.setTiffImageData(tiffImageData);
+
+        try (FileOutputStream fos = new FileOutputStream(outputFile);
+            BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+            final TiffImageWriterLossy writer = new 
TiffImageWriterLossy(byteOrder);
+            writer.write(bos, outputSet);
+            bos.flush();
+        }
+        return outputFile;
+    }
+
+    /**
+     * Gets the bytes for output for a 32 bit floating point format. Note that
+     * this method operates over "blocks" of data which may represent either
+     * TIFF Strips or Tiles. When processing strips, there is always one column
+     * of blocks and each strip is exactly the full width of the image. When
+     * processing tiles, there may be one or more columns of blocks and the
+     * block coverage may extend beyond both the last row and last column.
+     *
+     * @param f an array of the grid of output values in row major order
+     * @param width the width of the overall image
+     * @param height the height of the overall image
+     * @param nRowsInBlock the number of rows in the Strip or Tile
+     * @param nColsInBlock the number of columns in the Strip or Tile
+     * @param byteOrder little endian or big endian
+     * @return a valid array of equally sized array.
+     */
+    private byte[][] getBytesForOutput32(
+        final int nRowsInBlock, final int nColsInBlock,
+        ByteOrder byteOrder,
+        boolean useTiles, TiffPlanarConfiguration planarConfiguration ) {
+        final int nColsOfBlocks = (width + nColsInBlock - 1) / nColsInBlock;
+        final int nRowsOfBlocks = (height + nRowsInBlock + 1) / nRowsInBlock;
+        final int bytesPerPixel = 4 * samplesPerPixel;
+        final int nBlocks = nRowsOfBlocks * nColsOfBlocks;
+        final int nBytesInBlock = bytesPerPixel * nRowsInBlock * nColsInBlock;
+        final byte[][] blocks = new byte[nBlocks][nBytesInBlock];
+        if(planarConfiguration == TiffPlanarConfiguration.CHUNKY){
+            for (int i = 0; i < height; i++) {
+                final int blockRow = i / nRowsInBlock;
+                final int rowInBlock = i - blockRow * nRowsInBlock;
+                final int blockRowOffset = rowInBlock * nColsInBlock;
+                for (int j = 0; j < width; j++) {
+                    final int blockCol = j / nColsInBlock;
+                    final int colInBlock = j - blockCol * nColsInBlock;
+                    final byte[] b = blocks[blockRow * nColsOfBlocks + 
blockCol];  // reference to relevant block
+                    for(int k=0; k<2; k++){
+                        final float sValue = fSample[k*width*height + i * 
width + j];
+                        final int sample = Float.floatToRawIntBits(sValue);
+                        final int offset = (rowInBlock * nColsInBlock + 
colInBlock) * 8 + k * 4;
+                        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+                            b[offset] = (byte) (sample & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 3] = (byte) ((sample >> 24) & 0xff);
+                        } else {
+                            b[offset] = (byte) ((sample >> 24) & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 3] = (byte) (sample & 0xff);
+                        }
+                    }
+                }
+            }
+        }else{
+            for (int i = 0; i < height; i++) {
+                final int blockRow = i / nRowsInBlock;
+                final int rowInBlock = i - blockRow * nRowsInBlock;
+                int blockPlanarOffset = nRowsInBlock*nColsInBlock;
+                if(!useTiles && (blockRow+1)*nRowsInBlock>height){
+                    // For TIFF files using the Strip format, the convention
+                    // is to not include any extra padding in the data.  So if 
the
+                    // height of the image is not evenly divided by the number
+                    // of rows per strip, an adjustmnet is made to the size of 
the block.
+                    // However, the TIFF specification calls for tiles to 
always be padded.
+                     int nRowsAdjusted = height - blockRow*nRowsInBlock;
+                     blockPlanarOffset = nRowsAdjusted * nColsInBlock;
+                }
+                for (int j = 0; j < width; j++) {
+                    final int blockCol = j / nColsInBlock;
+                    final int colInBlock = j - blockCol * nColsInBlock;
+                    final byte[] b = blocks[blockRow * nColsOfBlocks + 
blockCol];  // reference to relevant block
+                    for(int k=0; k<2; k++){
+                        final float sValue = fSample[k*width*height + i * 
width + j];
+                        final int sample = Float.floatToRawIntBits(sValue);
+                        final int offset = (k * blockPlanarOffset + rowInBlock 
* nColsInBlock + colInBlock)* 4;
+                        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+                            b[offset] = (byte) (sample & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 3] = (byte) ((sample >> 24) & 0xff);
+                        } else {
+                            b[offset] = (byte) ((sample >> 24) & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 3] = (byte) (sample & 0xff);
+                        }
+                    }
+                }
+            }
+        }
+
+        return blocks;
+    }
+
+    private void applyTilePredictor(int nRowsInBlock, int nColsInBlock, byte[] 
bytes) {
+        // The floating-point horizonal predictor breaks the samples into
+        // separate sets of bytes.  The first set contains the high-order 
bytes.
+        // The second the second-highest order bytes, etc.  Once the bytes are
+        // separated, differencing is applied.  This treatment improves the
+        // statistical predictability of the data. By doing so, it improves
+        // its compressibility.
+        //     More extensive discussions of this technique are given in the
+        // Javadoc for the TIFF-specific ImageDataReader class.
+        byte[] b = new byte[bytes.length];
+        int bytesInRow = nColsInBlock * 4;
+        for (int iPlane = 0; iPlane < samplesPerPixel; iPlane++) {
+            // separate out the groups of bytes
+            int planarByteOffset = iPlane * nRowsInBlock * nColsInBlock * 4;
+            for (int i = 0; i < nRowsInBlock; i++) {
+                int aOffset = planarByteOffset + i * bytesInRow;
+                int bOffset = aOffset + nColsInBlock;
+                int cOffset = bOffset + nColsInBlock;
+                int dOffset = cOffset + nColsInBlock;
+                for (int j = 0; j < nColsInBlock; j++) {
+                    b[aOffset + j] = bytes[aOffset + j * 4];
+                    b[bOffset + j] = bytes[aOffset + j * 4 + 1];
+                    b[cOffset + j] = bytes[aOffset + j * 4 + 2];
+                    b[dOffset + j] = bytes[aOffset + j * 4 + 3];
+                }
+                // apply differencing
+                for (int j = bytesInRow - 1; j > 0; j--) {
+                    b[aOffset + j] -= b[aOffset + j - 1];
+                }
+            }
+        }
+        // copy the the results back over the input byte array
+        System.arraycopy(b, 0, bytes, 0, bytes.length);
+    }
+
+
+}

Review comment:
       I thought that the existing sample files already made the Commons 
Imaging distribution a bit larger than we would like. So I decided temporary 
files were a better way to do things.  Also, they have the side benefit of 
testing write operations.
   
   When I develop code, I always look for a source of images from some source 
outside the project. That way, we can be sure we are conforming to standard 
formats. 

##########
File path: 
src/test/java/org/apache/commons/imaging/formats/tiff/TiffFloatingPointMultivariableTest.java
##########
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.imaging.formats.tiff;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.awt.image.BufferedImage;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.commons.imaging.FormatCompliance;
+import org.apache.commons.imaging.ImageReadException;
+import org.apache.commons.imaging.ImageWriteException;
+import org.apache.commons.imaging.common.ImageBuilder;
+import org.apache.commons.imaging.common.bytesource.ByteSourceFile;
+import org.apache.commons.imaging.formats.tiff.constants.TiffConstants;
+import 
org.apache.commons.imaging.formats.tiff.constants.TiffPlanarConfiguration;
+import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
+import 
org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PhotometricInterpreterFloat;
+import org.apache.commons.imaging.formats.tiff.write.TiffImageWriterLossy;
+import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
+import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Performs a test in which a TIFF file with the special-purpose floating-point
+ * sample type is used to store data to a file. The file is then read to see if
+ * it matches the original values.
+ * <p>
+ * At this time, Commons Imaging does not fully implement the floating-point
+ * specification. Currently, this class only tests the use of uncompressed
+ * floating point values in the Strips format. The Tiles format is not
+ * exercised.
+ */
+public class TiffFloatingPointMultivariableTest extends TiffBaseTest {
+
+    @TempDir
+    Path tempDir;
+
+    int width = 48;
+    int height = 23;
+    int samplesPerPixel = 2;
+    float f0 = 0.0F;
+    float f1 = 1.0F;
+    float[] fSample = new float[width * height * samplesPerPixel];
+    public TiffFloatingPointMultivariableTest() {
+        for(int iPlane = 0; iPlane<2; iPlane++){
+            int pOffset = iPlane*width*height;
+            for(int iRow=0; iRow<height; iRow++){
+                for(int iCol=0; iCol<width; iCol++){
+                    int index = pOffset + iRow*width +iCol;
+                    fSample[index] = index;
+                }
+            }
+        }
+    }
+
+
+
+    @Test
+    public void test() throws Exception {
+        // we set up the 32 and 64 bit test cases.  At this time,
+        // the Tile format is not supported for floating-point samples by the
+        // TIFF datareaders classes.  So that format is not yet exercised.
+        // Note also that the compressed floating-point with predictor=3
+        // is processed in other tests, but not here.
+        List<File>testFiles = new ArrayList<>();
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, false, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    false, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, true, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    true, false, 
TiffPlanarConfiguration.CHUNKY));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, false, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    false, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.LITTLE_ENDIAN, true, false, 
TiffPlanarConfiguration.PLANAR));
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN,    true, false, 
TiffPlanarConfiguration.PLANAR));
+
+        // To exercise the horizontal-differencing-predictor logic, we include 
a writer that will
+        // reorganize the bytes into the form used by the floating-pont 
horizontal predictor.
+        // This test does not apply data compression, but it does apply the 
predictor.
+        // Note that although the TIFF predictor does not require big-endian 
formats, per se,
+        // the test logic implemented here does.
+        testFiles.add(writeFile(ByteOrder.BIG_ENDIAN, true, true, 
TiffPlanarConfiguration.PLANAR));
+
+        for(File testFile : testFiles){
+            final String name = testFile.getName();
+            final ByteSourceFile byteSource = new ByteSourceFile(testFile);
+            final TiffReader tiffReader = new TiffReader(true);
+            final TiffContents contents = tiffReader.readDirectories(
+                byteSource,
+                true, // indicates that application should read image data, if 
present
+                FormatCompliance.getDefault());
+            final TiffDirectory directory = contents.directories.get(0);
+            final HashMap<String, Object> params = new HashMap<>();
+            final ByteOrder byteOrder = tiffReader.getByteOrder();
+            final TiffRasterData raster = directory.getRasterData(params);
+            assertNotNull(raster, "Failed to get raster from " + name);
+            assertEquals(2, raster.getSamplesPerPixel(), "Invalid samples per 
pixel in " + name);
+            for(int iPlane = 0; iPlane<2; iPlane++){
+                int pOffset = iPlane*width*height;
+                for(int iRow=0; iRow<height; iRow++){
+                    for(int iCol=0; iCol<width; iCol++){
+                        int index = pOffset + iRow*width +iCol;
+                        float tValue = fSample[index];
+                        float rValue = raster.getValue(iCol, iRow, iPlane);
+                        assertEquals(tValue, rValue, "Failed at index 
x="+iCol+", y="+iRow+", iPlane="+iPlane);
+                    }
+                }
+            }
+        }
+    }
+
+    private File writeFile(
+        final ByteOrder byteOrder,
+        final boolean useTiles,
+        final boolean usePredictorForTiles,
+        TiffPlanarConfiguration planarConfiguration ) throws IOException, 
ImageWriteException {
+
+        final String name = String.format("FpMultiVarRoundTrip_%s_%s%s.tiff",
+            planarConfiguration==TiffPlanarConfiguration.CHUNKY ? "Chunky" : 
"Planar",
+            useTiles ? "Tiles" : "Strips",
+            usePredictorForTiles ? "_Predictor" : "");
+        final File outputFile = new File(tempDir.toFile(), name);
+
+        final int bytesPerSample = 4 * samplesPerPixel;
+        final int bitsPerSample =  8 * bytesPerSample;
+
+        int nRowsInBlock;
+        int nColsInBlock;
+        int nBytesInBlock;
+        if (useTiles) {
+            // Define the tiles so that they will not evenly subdivide
+            // the image.  This will allow the test to evaluate how the
+            // data reader processes tiles that are only partially used.
+            nRowsInBlock = 12;
+            nColsInBlock = 20;
+        } else {
+            // Define the strips so that they will not evenly subdivide
+            // the image.  This will allow the test to evaluate how the
+            // data reader processes strips that are only partially used.
+            nRowsInBlock = 2;
+            nColsInBlock = width;
+        }
+        nBytesInBlock = nRowsInBlock * nColsInBlock * bytesPerSample;
+
+        byte[][] blocks;
+            blocks = this.getBytesForOutput32(nRowsInBlock, nColsInBlock, 
byteOrder,
+            useTiles, planarConfiguration);
+
+        final TiffOutputSet outputSet = new TiffOutputSet(byteOrder);
+        final TiffOutputDirectory outDir = outputSet.addRootDirectory();
+        outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
+        outDir.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
+        outDir.add(TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT,
+            (short) TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT);
+        outDir.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL, (short) 
samplesPerPixel);
+        outDir.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, (short) 
bitsPerSample);
+        outDir.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION,
+            (short) 
TiffTagConstants.PHOTOMETRIC_INTERPRETATION_VALUE_BLACK_IS_ZERO);
+        outDir.add(TiffTagConstants.TIFF_TAG_COMPRESSION,
+            (short) TiffTagConstants.COMPRESSION_VALUE_UNCOMPRESSED);
+
+        if(useTiles && usePredictorForTiles){
+            outDir.add(TiffTagConstants.TIFF_TAG_PREDICTOR,
+            (short) 
TiffTagConstants.PREDICTOR_VALUE_FLOATING_POINT_DIFFERENCING);
+              for(int iBlock=0; iBlock<blocks.length; iBlock++){
+                  applyTilePredictor(nRowsInBlock, nColsInBlock, 
blocks[iBlock]);
+              }
+        }
+
+        if(planarConfiguration==TiffPlanarConfiguration.CHUNKY){
+        outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION,
+            (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY);
+        }else{
+               outDir.add(TiffTagConstants.TIFF_TAG_PLANAR_CONFIGURATION,
+            (short) TiffTagConstants.PLANAR_CONFIGURATION_VALUE_PLANAR);
+        }
+
+        if (useTiles) {
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_WIDTH, nColsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_LENGTH, nRowsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_TILE_BYTE_COUNTS, 
nBytesInBlock);
+        } else {
+            outDir.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP, nRowsInBlock);
+            outDir.add(TiffTagConstants.TIFF_TAG_STRIP_BYTE_COUNTS, 
nBytesInBlock);
+        }
+
+        final TiffElement.DataElement[] imageData = new 
TiffElement.DataElement[blocks.length];
+        for (int i = 0; i < blocks.length; i++) {
+            imageData[i] = new TiffImageData.Data(0, blocks[i].length, 
blocks[i]);
+        }
+
+        TiffImageData tiffImageData;
+        if (useTiles) {
+            tiffImageData
+                = new TiffImageData.Tiles(imageData, nColsInBlock, 
nRowsInBlock);
+        } else {
+            tiffImageData
+                = new TiffImageData.Strips(imageData, nRowsInBlock);
+        }
+        outDir.setTiffImageData(tiffImageData);
+
+        try (FileOutputStream fos = new FileOutputStream(outputFile);
+            BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+            final TiffImageWriterLossy writer = new 
TiffImageWriterLossy(byteOrder);
+            writer.write(bos, outputSet);
+            bos.flush();
+        }
+        return outputFile;
+    }
+
+    /**
+     * Gets the bytes for output for a 32 bit floating point format. Note that
+     * this method operates over "blocks" of data which may represent either
+     * TIFF Strips or Tiles. When processing strips, there is always one column
+     * of blocks and each strip is exactly the full width of the image. When
+     * processing tiles, there may be one or more columns of blocks and the
+     * block coverage may extend beyond both the last row and last column.
+     *
+     * @param f an array of the grid of output values in row major order
+     * @param width the width of the overall image
+     * @param height the height of the overall image
+     * @param nRowsInBlock the number of rows in the Strip or Tile
+     * @param nColsInBlock the number of columns in the Strip or Tile
+     * @param byteOrder little endian or big endian
+     * @return a valid array of equally sized array.
+     */
+    private byte[][] getBytesForOutput32(
+        final int nRowsInBlock, final int nColsInBlock,
+        ByteOrder byteOrder,
+        boolean useTiles, TiffPlanarConfiguration planarConfiguration ) {
+        final int nColsOfBlocks = (width + nColsInBlock - 1) / nColsInBlock;
+        final int nRowsOfBlocks = (height + nRowsInBlock + 1) / nRowsInBlock;
+        final int bytesPerPixel = 4 * samplesPerPixel;
+        final int nBlocks = nRowsOfBlocks * nColsOfBlocks;
+        final int nBytesInBlock = bytesPerPixel * nRowsInBlock * nColsInBlock;
+        final byte[][] blocks = new byte[nBlocks][nBytesInBlock];
+        if(planarConfiguration == TiffPlanarConfiguration.CHUNKY){
+            for (int i = 0; i < height; i++) {
+                final int blockRow = i / nRowsInBlock;
+                final int rowInBlock = i - blockRow * nRowsInBlock;
+                final int blockRowOffset = rowInBlock * nColsInBlock;
+                for (int j = 0; j < width; j++) {
+                    final int blockCol = j / nColsInBlock;
+                    final int colInBlock = j - blockCol * nColsInBlock;
+                    final byte[] b = blocks[blockRow * nColsOfBlocks + 
blockCol];  // reference to relevant block
+                    for(int k=0; k<2; k++){
+                        final float sValue = fSample[k*width*height + i * 
width + j];
+                        final int sample = Float.floatToRawIntBits(sValue);
+                        final int offset = (rowInBlock * nColsInBlock + 
colInBlock) * 8 + k * 4;
+                        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+                            b[offset] = (byte) (sample & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 3] = (byte) ((sample >> 24) & 0xff);
+                        } else {
+                            b[offset] = (byte) ((sample >> 24) & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 3] = (byte) (sample & 0xff);
+                        }
+                    }
+                }
+            }
+        }else{
+            for (int i = 0; i < height; i++) {
+                final int blockRow = i / nRowsInBlock;
+                final int rowInBlock = i - blockRow * nRowsInBlock;
+                int blockPlanarOffset = nRowsInBlock*nColsInBlock;
+                if(!useTiles && (blockRow+1)*nRowsInBlock>height){
+                    // For TIFF files using the Strip format, the convention
+                    // is to not include any extra padding in the data.  So if 
the
+                    // height of the image is not evenly divided by the number
+                    // of rows per strip, an adjustmnet is made to the size of 
the block.
+                    // However, the TIFF specification calls for tiles to 
always be padded.
+                     int nRowsAdjusted = height - blockRow*nRowsInBlock;
+                     blockPlanarOffset = nRowsAdjusted * nColsInBlock;
+                }
+                for (int j = 0; j < width; j++) {
+                    final int blockCol = j / nColsInBlock;
+                    final int colInBlock = j - blockCol * nColsInBlock;
+                    final byte[] b = blocks[blockRow * nColsOfBlocks + 
blockCol];  // reference to relevant block
+                    for(int k=0; k<2; k++){
+                        final float sValue = fSample[k*width*height + i * 
width + j];
+                        final int sample = Float.floatToRawIntBits(sValue);
+                        final int offset = (k * blockPlanarOffset + rowInBlock 
* nColsInBlock + colInBlock)* 4;
+                        if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+                            b[offset] = (byte) (sample & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 3] = (byte) ((sample >> 24) & 0xff);
+                        } else {
+                            b[offset] = (byte) ((sample >> 24) & 0xff);
+                            b[offset + 1] = (byte) ((sample >> 16) & 0xff);
+                            b[offset + 2] = (byte) ((sample >> 8) & 0xff);
+                            b[offset + 3] = (byte) (sample & 0xff);
+                        }
+                    }
+                }
+            }
+        }
+
+        return blocks;
+    }
+
+    private void applyTilePredictor(int nRowsInBlock, int nColsInBlock, byte[] 
bytes) {
+        // The floating-point horizonal predictor breaks the samples into
+        // separate sets of bytes.  The first set contains the high-order 
bytes.
+        // The second the second-highest order bytes, etc.  Once the bytes are
+        // separated, differencing is applied.  This treatment improves the
+        // statistical predictability of the data. By doing so, it improves
+        // its compressibility.
+        //     More extensive discussions of this technique are given in the
+        // Javadoc for the TIFF-specific ImageDataReader class.
+        byte[] b = new byte[bytes.length];
+        int bytesInRow = nColsInBlock * 4;
+        for (int iPlane = 0; iPlane < samplesPerPixel; iPlane++) {
+            // separate out the groups of bytes
+            int planarByteOffset = iPlane * nRowsInBlock * nColsInBlock * 4;
+            for (int i = 0; i < nRowsInBlock; i++) {
+                int aOffset = planarByteOffset + i * bytesInRow;
+                int bOffset = aOffset + nColsInBlock;
+                int cOffset = bOffset + nColsInBlock;
+                int dOffset = cOffset + nColsInBlock;
+                for (int j = 0; j < nColsInBlock; j++) {
+                    b[aOffset + j] = bytes[aOffset + j * 4];
+                    b[bOffset + j] = bytes[aOffset + j * 4 + 1];
+                    b[cOffset + j] = bytes[aOffset + j * 4 + 2];
+                    b[dOffset + j] = bytes[aOffset + j * 4 + 3];
+                }
+                // apply differencing
+                for (int j = bytesInRow - 1; j > 0; j--) {
+                    b[aOffset + j] -= b[aOffset + j - 1];
+                }
+            }
+        }
+        // copy the the results back over the input byte array
+        System.arraycopy(b, 0, bytes, 0, bytes.length);
+    }
+
+
+}

Review comment:
       I thought that the existing sample files already made the Commons 
Imaging distribution a bit larger than we would like. So I decided temporary 
files were a better way to do things.  Also, they have the side benefit of 
testing write operations.
   
   When I develop code, I always look for a source of images from some source 
outside the project. That way, we can be sure we are conforming to standard 
formats. 
   
   Also, extra lines are removed (done).

##########
File path: 
src/main/java/org/apache/commons/imaging/formats/tiff/TiffRasterDataInt.java
##########
@@ -23,14 +23,24 @@
  * <p>
  * <strong>Note:</strong> The getData() and getIntData() methods can return
  * direct references to the internal arrays stored in instances of this class.
- * Because these are not safe copies of the data, an application that
- * modified the arrays returned by these methods will change the content
- * of the associated instance. This approach is used for purposes of efficiency
- * when dealing with very large TIFF images.
+ * Because these are not safe copies of the data, an application that modified
+ * the arrays returned by these methods will change the content of the
+ * associated instance. This approach is used for purposes of efficiency when
+ * dealing with very large TIFF images.
+ * <p>
+ * <strong>Data layout:</strong> The elements in the returned array are stored
+ * in row-major order. In cases where the data contains multiple samples per
+ * raster cell (pixel), the data is organized into blocks of data one sample at
+ * a time. The first block contains width*height values for the first sample 
for
+ * each cell, the second block contains width*height values for the second
+ * sample for each cell, etc. Thus, the array index for a particular value is
+ * computed as
+ * <pre>
+ *    index = y * width + x + iSample * width * height;
+ * </pre>

Review comment:
       Following your suggestion, I experimented with this, but I did not have 
much luck. Except in cases where a verbatim of the parent documentation was 
desired, I couldn't get it to work.
   
   I will look at this some more and see if I can get it done for a future PR.  
I am thinking that a "Javadoc PR" may be worth doing some time in the future.  
So this would be part of it.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@commons.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


Reply via email to