    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.imaging.formats.tiff;
    018
    019import java.util.stream.IntStream;
    020
    021/**
    022 * Provides a simple container for floating-point data. Some TIFF files are used
    023 * to store floating-point data rather than images. This class is intended to
    024 * support access to those TIFF files.
    025 * <p>
    026 * <strong>Note:</strong> The getData() and getIntData() methods can return
    027 * direct references to the internal arrays stored in instances of this class.
    028 * Because these are not safe copies of the data, an application that modified
    029 * the arrays returned by these methods will change the content of the
    030 * associated instance. This approach is used for purposes of efficiency when
    031 * dealing with very large TIFF images.
    032 * <p>
    033 * <strong>Data layout:</strong> The elements in the returned array are stored
    034 * in row-major order. In cases where the data contains multiple samples per
    035 * raster cell (pixel), the data is organized into blocks of data one sample at
    036 * a time. The first block contains width*height values for the first sample for
    037 * each cell, the second block contains width*height values for the second
    038 * sample for each cell, etc. Thus, the array index for a particular value is
    039 * computed as
    040 * <pre>
    041 *    index = y*width + x + iSample * width *height;
    042 * </pre>
    043 */
    044public class TiffRasterDataFloat extends TiffRasterData {
    045
    046    private final float[] data;
    047
    048    /**
    049     * Construct an instance allocating memory for the specified dimensions.
    050     *
    051     * @param width a value of 1 or greater
    052     * @param height a value of 1 or greater
    053     */
    054    public TiffRasterDataFloat(final int width, final int height) {
    055        super(width, height, 1);
    056        data = new float[nCells];
    057    }
    058
    059    /**
    060     * Construct an instance allocating memory for the specified dimensions.
    061     *
    062     * @param width a value of 1 or greater
    063     * @param height a value of 1 or greater
    064     * @param samplesPerPixel a value of 1 or greater
    065     */
    066    public TiffRasterDataFloat(final int width, final int height, int samplesPerPixel) {
    067        super(width, height, samplesPerPixel);
    068        data = new float[nCells];
    069    }
    070
    071    /**
    072     * Construct an instance allocating memory for the specified dimensions.
    073     *
    074     * @param width a value of 1 or greater
    075     * @param height a value of 1 or greater
    076     * @param data the data to be stored in the raster.
    077     */
    078    public TiffRasterDataFloat(final int width, final int height, final float[] data) {
    079        super(width, height, 1);
    080
    081        if (data == null || data.length < nCells) {
    082            throw new IllegalArgumentException(
    083                    "Specified data does not contain sufficient elements");
    084        }
    085        this.data = data;
    086    }
    087
    088    /**
    089     * Construct an instance allocating memory for the specified dimensions.
    090     *
    091     * @param width a value of 1 or greater
    092     * @param height a value of 1 or greater
    093     * @param samplesPerCell the number of samples per pixel
    094     * @param data the data to be stored in the raster.
    095     */
    096    public TiffRasterDataFloat(final int width, final int height, int samplesPerCell, final float[] data) {
    097        super(width, height, samplesPerCell);
    098
    099        if (data == null || data.length < nCells) {
    100            throw new IllegalArgumentException(
    101                "Specified data does not contain sufficient elements");
    102        }
    103        this.data = data;
    104    }
    105
    106    /**
    107     * Gets the raster data type from the instance.
    108     *
    109     * @return a value of TiffRasterDataType&#46;FLOAT.
    110     */
    111    @Override
    112    public TiffRasterDataType getDataType() {
    113        return TiffRasterDataType.FLOAT;
    114    }
    115
    116    /**
    117     * Sets the value stored at the specified raster coordinates.
    118     *
    119     * @param x integer coordinate in the columnar direction
    120     * @param y integer coordinate in the row direction
    121     * @param value the value to be stored at the specified location;
    122     * potentially a Float&#46;NaN.
    123     */
    124    @Override
    125    public void setValue(final int x, final int y, final float value) {
    126        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    127        data[index] = value;
    128    }
    129
    130    /**
    131     * Sets the value stored at the specified raster coordinates.
    132     *
    133     * @param x integer coordinate in the columnar direction
    134     * @param y integer coordinate in the row direction
    135     * @param i integer sample index (for data sets giving multiple samples per
    136     * raster cell).
    137     * @param value the value to be stored at the specified location;
    138     * potentially a Float&#46;NaN.
    139     */
    140    @Override
    141    public void setValue(final int x, final int y, int i, final float value) {
    142        int index = checkCoordinatesAndComputeIndex(x, y, i);
    143        data[index] = value;
    144    }
    145
    146    /**
    147     * Gets the value stored at the specified raster coordinates.
    148     *
    149     * @param x integer coordinate in the columnar direction
    150     * @param y integer coordinate in the row direction
    151     * @return the value stored at the specified location; potentially a
    152     * Float&#46;NaN.
    153     */
    154    @Override
    155    public float getValue(final int x, final int y) {
    156        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    157        return data[index];
    158    }
    159
    160    /**
    161     * Gets the value stored at the specified raster coordinates.
    162     *
    163     * @param x integer coordinate in the columnar direction
    164     * @param y integer coordinate in the row direction
    165     * @param i integer sample index (for data sets giving multiple samples per
    166     * raster cell).
    167     * @return the value stored at the specified location; potentially a
    168     * Float&#46;NaN.
    169     */
    170    @Override
    171    public float getValue(final int x, final int y, int i) {
    172        int index = checkCoordinatesAndComputeIndex(x, y, i);
    173        return data[index];
    174    }
    175
    176    /**
    177     * Sets the value stored at the specified raster coordinates.
    178     *
    179     * @param x integer coordinate in the columnar direction
    180     * @param y integer coordinate in the row direction
    181     * @param value the value to be stored at the specified location
    182     */
    183    @Override
    184    public void setIntValue(final int x, final int y, final int value) {
    185        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    186        data[index] = value;
    187    }
    188
    189    /**
    190     * Sets the value stored at the specified raster coordinates.
    191     *
    192     * @param x integer coordinate in the columnar direction
    193     * @param y integer coordinate in the row direction
    194     * @param i integer sample index (for data sets giving multiple samples per
    195     * raster cell).
    196     * @param value the value to be stored at the specified location
    197     */
    198    @Override
    199    public void setIntValue(final int x, final int y, int i, final int value) {
    200        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    201        data[index] = value;
    202    }
    203
    204    /**
    205     * Gets the value stored at the specified raster coordinates.
    206     *
    207     * @param x integer coordinate in the columnar direction
    208     * @param y integer coordinate in the row direction
    209     * @return the value stored at the specified location
    210     */
    211    @Override
    212    public int getIntValue(final int x, final int y) {
    213        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    214        return (int) data[index];
    215    }
    216
    217    /**
    218     * Gets the value stored at the specified raster coordinates.
    219     *
    220     * @param x integer coordinate in the columnar direction
    221     * @param y integer coordinate in the row direction
    222     * @param i integer sample index (for data sets giving multiple samples per
    223     * raster cell).
    224     * @return the value stored at the specified location
    225     */
    226    @Override
    227    public int getIntValue(final int x, final int y, int i) {
    228        int index = checkCoordinatesAndComputeIndex(x, y, 0);
    229        return (int) data[index];
    230    }
    231
    232    /**
    233     * Tabulates simple statistics for the raster and returns an instance
    234     * containing general metadata.
    235     *
    236     * @return a valid instance containing a safe copy of the current simple
    237     * statistics for the raster.
    238     */
    239    @Override
    240    public TiffRasterStatistics getSimpleStatistics() {
    241        return new TiffRasterStatistics(this, Float.NaN);
    242    }
    243
    244    /**
    245     * Tabulates simple statistics for the raster excluding the specified value
    246     * and returns an instance containing general metadata.
    247     *
    248     * @param valueToExclude exclude samples with this specified value.
    249     * @return a valid instance.
    250     */
    251    @Override
    252    public TiffRasterStatistics getSimpleStatistics(final float valueToExclude) {
    253        return new TiffRasterStatistics(this, valueToExclude);
    254    }
    255
    256    /**
    257     * Returns a reference to the data array stored in this instance. Note that
    258     * the array returned is <strong>not</strong> a safe copy and that modifying
    259     * it directly affects the content of the instance. While this design
    260     * approach carries some risk in terms of data security, it was chosen for
    261     * reasons of performance and memory conservation. TIFF images that contain
    262     * floating-point data are often quite large. Sizes of 100 million raster
    263     * cells are common. Making a redundant copy of such a large in-memory
    264     * object might exceed the resources available to a Java application.
    265     * <p>
    266     * See the class API documentation above for notes on accessing array
    267     * elements.
    268     *
    269     * @return a direct reference to the data array stored in this instance.
    270     */
    271    @Override
    272    public float[] getData() {
    273        return data;
    274    }
    275
    276    /**
    277     * Returns an array of integer approximations for the floating-point content
    278     * stored as an array in this instance.
    279     * <p>
    280     * See the class API documentation above for notes on accessing array
    281     * elements.
    282     *
    283     * @return the integer equivalents to the data content stored in this
    284     * instance.
    285     */
    286    @Override
    287    public int[] getIntData() {
    288        return IntStream.range(0, nCells)
    289                .map(i -> (int) data[i])
    290                .toArray();
    291    }
    292
    293}