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
021import org.apache.commons.imaging.common.Allocator;
022
023/**
024 * Provides a simple container for floating-point data. Some TIFF files are used to store floating-point data rather than images. This class is intended to
025 * support access to those TIFF files.
026 * <p>
027 * <strong>Note:</strong> The getData() and getIntData() methods can return direct references to the internal arrays stored in instances of this class. Because
028 * 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.
029 * This approach is used for purposes of efficiency when dealing with very large TIFF images.
030 * <p>
031 * <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
032 * 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
033 * 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
034 *
035 * <pre>
036 * index = y * width + x + iSample * width * height;
037 * </pre>
038 */
039public class TiffRasterDataFloat extends TiffRasterData {
040
041    private final float[] data;
042
043    /**
044     * Constructs an instance allocating memory for the specified dimensions.
045     *
046     * @param width  a value of 1 or greater
047     * @param height a value of 1 or greater
048     */
049    public TiffRasterDataFloat(final int width, final int height) {
050        super(width, height, 1);
051        data = Allocator.floatArray(nCells);
052    }
053
054    /**
055     * Constructs an instance allocating memory for the specified dimensions.
056     *
057     * @param width  a value of 1 or greater
058     * @param height a value of 1 or greater
059     * @param data   the data to be stored in the raster.
060     */
061    public TiffRasterDataFloat(final int width, final int height, final float[] data) {
062        super(width, height, 1);
063
064        if (data == null || data.length < nCells) {
065            throw new IllegalArgumentException("Specified data does not contain sufficient elements");
066        }
067        this.data = data;
068    }
069
070    /**
071     * Constructs an instance allocating memory for the specified dimensions.
072     *
073     * @param width           a value of 1 or greater
074     * @param height          a value of 1 or greater
075     * @param samplesPerPixel a value of 1 or greater
076     */
077    public TiffRasterDataFloat(final int width, final int height, final int samplesPerPixel) {
078        super(width, height, samplesPerPixel);
079        data = Allocator.floatArray(nCells);
080    }
081
082    /**
083     * Constructs an instance allocating memory for the specified dimensions.
084     *
085     * @param width          a value of 1 or greater
086     * @param height         a value of 1 or greater
087     * @param samplesPerCell the number of samples per pixel
088     * @param data           the data to be stored in the raster.
089     */
090    public TiffRasterDataFloat(final int width, final int height, final int samplesPerCell, final float[] data) {
091        super(width, height, samplesPerCell);
092
093        if (data == null || data.length < nCells) {
094            throw new IllegalArgumentException("Specified data does not contain sufficient elements");
095        }
096        this.data = data;
097    }
098
099    /**
100     * Returns a reference to the data array stored in this instance. Note that the array returned is <strong>not</strong> a safe copy and that modifying it
101     * directly affects the content of the instance. While this design approach carries some risk in terms of data security, it was chosen for reasons of
102     * performance and memory conservation. TIFF images that contain floating-point data are often quite large. Sizes of 100 million raster cells are common.
103     * Making a redundant copy of such a large in-memory object might exceed the resources available to a Java application.
104     * <p>
105     * See the class API documentation above for notes on accessing array elements.
106     *
107     * @return a direct reference to the data array stored in this instance.
108     */
109    @Override
110    public float[] getData() {
111        return data;
112    }
113
114    /**
115     * Gets the raster data type from the instance.
116     *
117     * @return a value of TiffRasterDataType&#46;FLOAT.
118     */
119    @Override
120    public TiffRasterDataType getDataType() {
121        return TiffRasterDataType.FLOAT;
122    }
123
124    /**
125     * Returns an array of integer approximations for the floating-point content stored as an array in this instance.
126     * <p>
127     * See the class API documentation above for notes on accessing array elements.
128     *
129     * @return the integer equivalents to the data content stored in this instance.
130     */
131    @Override
132    public int[] getIntData() {
133        return IntStream.range(0, nCells).map(i -> (int) data[i]).toArray();
134    }
135
136    /**
137     * Gets the value stored at the specified raster coordinates.
138     *
139     * @param x integer coordinate in the columnar direction
140     * @param y integer coordinate in the row direction
141     * @return the value stored at the specified location
142     */
143    @Override
144    public int getIntValue(final int x, final int y) {
145        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
146        return (int) data[index];
147    }
148
149    /**
150     * Gets the value stored at the specified raster coordinates.
151     *
152     * @param x integer coordinate in the columnar direction
153     * @param y integer coordinate in the row direction
154     * @param i integer sample index (for data sets giving multiple samples per raster cell).
155     * @return the value stored at the specified location
156     */
157    @Override
158    public int getIntValue(final int x, final int y, final int i) {
159        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
160        return (int) data[index];
161    }
162
163    /**
164     * Tabulates simple statistics for the raster and returns an instance containing general metadata.
165     *
166     * @return a valid instance containing a safe copy of the current simple statistics for the raster.
167     */
168    @Override
169    public TiffRasterStatistics getSimpleStatistics() {
170        return new TiffRasterStatistics(this, Float.NaN);
171    }
172
173    /**
174     * Tabulates simple statistics for the raster excluding the specified value and returns an instance containing general metadata.
175     *
176     * @param valueToExclude exclude samples with this specified value.
177     * @return a valid instance.
178     */
179    @Override
180    public TiffRasterStatistics getSimpleStatistics(final float valueToExclude) {
181        return new TiffRasterStatistics(this, valueToExclude);
182    }
183
184    /**
185     * Gets the value stored at the specified raster coordinates.
186     *
187     * @param x integer coordinate in the columnar direction
188     * @param y integer coordinate in the row direction
189     * @return the value stored at the specified location; potentially a Float&#46;NaN.
190     */
191    @Override
192    public float getValue(final int x, final int y) {
193        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
194        return data[index];
195    }
196
197    /**
198     * Gets the value stored at the specified raster coordinates.
199     *
200     * @param x integer coordinate in the columnar direction
201     * @param y integer coordinate in the row direction
202     * @param i integer sample index (for data sets giving multiple samples per raster cell).
203     * @return the value stored at the specified location; potentially a Float&#46;NaN.
204     */
205    @Override
206    public float getValue(final int x, final int y, final int i) {
207        final int index = checkCoordinatesAndComputeIndex(x, y, i);
208        return data[index];
209    }
210
211    /**
212     * Sets the value stored at the specified raster coordinates.
213     *
214     * @param x     integer coordinate in the columnar direction
215     * @param y     integer coordinate in the row direction
216     * @param value the value to be stored at the specified location
217     */
218    @Override
219    public void setIntValue(final int x, final int y, final int value) {
220        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
221        data[index] = value;
222    }
223
224    /**
225     * Sets the value stored at the specified raster coordinates.
226     *
227     * @param x     integer coordinate in the columnar direction
228     * @param y     integer coordinate in the row direction
229     * @param i     integer sample index (for data sets giving multiple samples per raster cell).
230     * @param value the value to be stored at the specified location
231     */
232    @Override
233    public void setIntValue(final int x, final int y, final int i, final int value) {
234        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
235        data[index] = value;
236    }
237
238    /**
239     * Sets the value stored at the specified raster coordinates.
240     *
241     * @param x     integer coordinate in the columnar direction
242     * @param y     integer coordinate in the row direction
243     * @param value the value to be stored at the specified location; potentially a Float&#46;NaN.
244     */
245    @Override
246    public void setValue(final int x, final int y, final float value) {
247        final int index = checkCoordinatesAndComputeIndex(x, y, 0);
248        data[index] = value;
249    }
250
251    /**
252     * Sets the value stored at the specified raster coordinates.
253     *
254     * @param x     integer coordinate in the columnar direction
255     * @param y     integer coordinate in the row direction
256     * @param i     integer sample index (for data sets giving multiple samples per raster cell).
257     * @param value the value to be stored at the specified location; potentially a Float&#46;NaN.
258     */
259    @Override
260    public void setValue(final int x, final int y, final int i, final float value) {
261        final int index = checkCoordinatesAndComputeIndex(x, y, i);
262        data[index] = value;
263    }
264
265}