001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.object;
016
017import java.util.Iterator;
018import java.util.List;
019import java.util.Vector;
020
021import org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024/**
025 * A scalar dataset is a multiple dimension array of scalar points. The Datatype of a scalar dataset must be an atomic
026 * datatype. Common datatypes of scalar datasets include char, byte, short, int, long, float, double and string.
027 *
028 * A ScalarDS can be an image or spreadsheet data. ScalarDS defines methods to deal with both images and
029 * spreadsheets.
030 *
031 * ScalarDS is an abstract class. Current implementing classes are the H4SDS, H5GRImage and H5ScalarDS.
032 *
033 * @version 1.1 9/4/2007
034 * @author Peter X. Cao
035 */
036public abstract class ScalarDS extends Dataset
037{
038    private static final long serialVersionUID = 8925371455928203981L;
039
040    private static final Logger log = LoggerFactory.getLogger(ScalarDS.class);
041
042    /************************************************************
043     * The following constant strings are copied from * https://hdfgroup.github.io/hdf5/_i_m_g.html * to make the
044     * definition consistent with the image specs. *
045     ************************************************************/
046
047    /**
048     * Indicates that the pixel RGB values are contiguous.
049     */
050    public static final int INTERLACE_PIXEL = 0;
051
052    /** Indicates that each pixel component of RGB is stored as a scan line. */
053    public static final int INTERLACE_LINE = 1;
054
055    /** Indicates that each pixel component of RGB is stored as a plane. */
056    public static final int INTERLACE_PLANE = 2;
057
058    /**
059     * The interlace mode of the stored raster image data. Valid values are INTERLACE_PIXEL, INTERLACE_LINE and
060     * INTERLACE_PLANE.
061     */
062    protected int interlace;
063
064    /**
065     * The min-max range of image data values. For example, [0, 255] indicates the min is 0, and the max is 255.
066     */
067    protected double[] imageDataRange;
068
069    /**
070     * The indexed RGB color model with 256 colors.
071     *
072     * The palette values are stored in a two-dimensional byte array and arrange by color components of red, green and
073     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
074     * components respectively.
075     */
076    protected byte[][] palette;
077
078    /**
079     * True if this dataset is a true color image.
080     */
081    protected boolean isTrueColor;
082
083    /**
084     * Flag to indicate is the original unsigned C data is converted.
085     */
086    protected boolean unsignedConverted;
087
088    /** The fill value of the dataset. */
089    protected Object fillValue = null;
090
091    /** The list of filtered image values. */
092    private List<Number> filteredImageValues;
093
094    /** Flag to indicate if the dataset is displayed as an image. */
095    protected boolean isImageDisplay;
096
097    /**
098     * Flag to indicate if the dataset is displayed as an image with default order of dimensions.
099     */
100    protected boolean isDefaultImageOrder;
101
102    /**
103     * Flag to indicate if the FillValue is converted from unsigned C.
104     */
105    public boolean isFillValueConverted;
106
107    /**
108     * Constructs an instance of a ScalarDS with specific name and path. An HDF data object must have a name. The path
109     * is the group path starting from the root.
110     *
111     * For example, in H5ScalarDS(h5file, "dset", "/arrays/"), "dset" is the name of the dataset, "/arrays" is the group
112     * path of the dataset.
113     *
114     * @param theFile
115     *            the file that contains the data object.
116     * @param theName
117     *            the name of the data object, e.g. "dset".
118     * @param thePath
119     *            the full path of the data object, e.g. "/arrays/".
120     */
121    public ScalarDS(FileFormat theFile, String theName, String thePath) {
122        this(theFile, theName, thePath, null);
123    }
124
125    /**
126     * @deprecated Not for public use in the future.<br>
127     *             Using {@link #ScalarDS(FileFormat, String, String)}
128     *
129     * @param theFile
130     *            the file that contains the data object.
131     * @param theName
132     *            the name of the data object, e.g. "dset".
133     * @param thePath
134     *            the full path of the data object, e.g. "/arrays/".
135     * @param oid
136     *            the object id of the data object.
137     */
138    @Deprecated
139    public ScalarDS(FileFormat theFile, String theName, String thePath, long[] oid) {
140        super(theFile, theName, thePath, oid);
141
142        palette = null;
143        isImage = false;
144        isTrueColor = false;
145        isText = false;
146        interlace = -1;
147        imageDataRange = null;
148        isImageDisplay = false;
149        isDefaultImageOrder = true;
150        isFillValueConverted = false;
151        filteredImageValues = new Vector<>();
152    }
153
154    /**
155     * Clears the current data buffer in memory and forces the next read() to load
156     * the data from file.
157     *
158     * The function read() loads data from file into memory only if the data is not
159     * read. If data is already in memory, read() just returns the memory buffer.
160     * Sometimes we want to force read() to re-read data from file. For example,
161     * when the selection is changed, we need to re-read the data.
162     *
163     * @see #getData()
164     * @see #read()
165     */
166    @Override
167    public void clearData() {
168        super.clearData();
169        unsignedConverted = false;
170    }
171
172    /**
173     * Converts the data values of this dataset to appropriate Java integer if they are unsigned integers.
174     *
175     * @see hdf.object.Dataset#convertToUnsignedC(Object)
176     * @see hdf.object.Dataset#convertFromUnsignedC(Object, Object)
177     *
178     * @return the converted data buffer.
179     */
180    @Override
181    public Object convertFromUnsignedC() {
182        // keep a copy of original buffer and the converted buffer
183        // so that they can be reused later to save memory
184        log.trace("convertFromUnsignedC(): unsigned={}", getDatatype().isUnsigned());
185        if ((data != null) && getDatatype().isUnsigned() && !unsignedConverted) {
186            log.trace("convertFromUnsignedC(): convert");
187            originalBuf = data;
188            convertedBuf = convertFromUnsignedC(originalBuf, convertedBuf);
189            data = convertedBuf;
190            unsignedConverted = true;
191
192            if (fillValue != null) {
193                if (!isFillValueConverted) {
194                    fillValue = convertFromUnsignedC(fillValue, null);
195                    isFillValueConverted = true;
196                }
197            }
198        }
199
200        return data;
201    }
202
203    /**
204     * Converts Java integer data of this dataset back to unsigned C-type integer data if they are unsigned integers.
205     *
206     * @see hdf.object.Dataset#convertToUnsignedC(Object)
207     * @see hdf.object.Dataset#convertToUnsignedC(Object, Object)
208     * @see #convertFromUnsignedC(Object data_in)
209     *
210     * @return the converted data buffer.
211     */
212    @Override
213    public Object convertToUnsignedC() {
214        // keep a copy of original buffer and the converted buffer
215        // so that they can be reused later to save memory
216        log.trace("convertToUnsignedC(): unsigned={}", getDatatype().isUnsigned());
217        if ((data != null) && getDatatype().isUnsigned()) {
218            log.trace("convertToUnsignedC(): convert");
219            convertedBuf = data;
220            originalBuf = convertToUnsignedC(convertedBuf, originalBuf);
221            data = originalBuf;
222        }
223
224        return data;
225    }
226
227    /**
228     * Returns the palette of this scalar dataset or null if palette does not exist.
229     *
230     * A Scalar dataset can be displayed as spreadsheet data or an image. When a scalar dataset is displayed as an
231     * image, the palette or color table may be needed to translate a pixel value to color components (for example, red,
232     * green, and blue). Some scalar datasets have no palette and some datasets have one or more than one palettes. If
233     * an associated palette exists but is not loaded, this interface retrieves the palette from the file and returns the
234     * palette. If the palette is loaded, it returns the palette. It returns null if there is no palette associated with
235     * the dataset.
236     *
237     * Current implementation only supports palette model of indexed RGB with 256 colors. Other models such as
238     * YUV", "CMY", "CMYK", "YCbCr", "HSV will be supported in the future.
239     *
240     * The palette values are stored in a two-dimensional byte array and are arranges by color components of red, green and
241     * blue. palette[][] = byte[3][256], where, palette[0][], palette[1][] and palette[2][] are the red, green and blue
242     * components respectively.
243     *
244     * Sub-classes have to implement this interface. HDF4 and HDF5 images use different libraries to retrieve the
245     * associated palette.
246     *
247     * @return the 2D palette byte array.
248     */
249    public byte[][] getPalette() {
250        return palette;
251    }
252
253    /**
254     * Sets the palette for this dataset.
255     *
256     * @param pal
257     *            the 2D palette byte array.
258     */
259    public final void setPalette(byte[][] pal) {
260        palette = pal;
261    }
262
263    /**
264     * Reads a specific image palette from file.
265     *
266     * A scalar dataset may have multiple palettes attached to it. readPalette(int idx) returns a specific palette
267     * identified by its index.
268     *
269     * @param idx
270     *            the index of the palette to read.
271     *
272     * @return the image palette
273     */
274    public byte[][] readPalette(int idx) {
275        return null;
276    }
277
278    /**
279     * Get the name of a specific image palette from file.
280     *
281     * A scalar dataset may have multiple palettes attached to it. getPaletteName(int idx) returns the name of a
282     * specific palette identified by its index.
283     *
284     * @param idx
285     *            the index of the palette to retrieve the name.
286     *
287     * @return The name of the palette
288     */
289    public String getPaletteName(int idx) {
290        String paletteName = "Default ";
291        if (idx != 0)
292            paletteName = "Default " + idx;
293        return paletteName;
294    }
295
296    /**
297     * Get the number of pallettes for this object.
298     *
299     * @return the number of palettes if it has any,
300     *         0 if there is no palette attribute attached to this dataset.
301     */
302    public int getNumberOfPalettes() {
303        return 0;
304    }
305
306    /**
307     * Returns true if this dataset is an image.
308     *
309     * For all Images, they must have an attribute called "CLASS". The value of this attribute is "IMAGE". For more
310     * details, read <a href="https://hdfgroup.github.io/hdf5/_i_m_g.html"> HDF5 Image and Palette Specification</a>
311     *
312     * @return true if the dataset is an image; otherwise, returns false.
313     */
314    public final boolean isImage() {
315        return isImage;
316    }
317
318    /**
319     * Returns true if this dataset is displayed as an image.
320     *
321     * A ScalarDS can be displayed as an image or a spreadsheet in a table.
322     *
323     * @return true if this dataset is displayed as an image; otherwise, returns false.
324     */
325    public final boolean isImageDisplay() {
326
327        return isImageDisplay;
328    }
329
330    /**
331     * Returns true if this dataset is displayed as an image with default image order.
332     *
333     * A ScalarDS can be displayed as an image with different orders of dimensions.
334     *
335     * @return true if this dataset is displayed as an image with default image order; otherwise, returns false.
336     */
337    public final boolean isDefaultImageOrder() {
338        return isDefaultImageOrder;
339    }
340
341    /**
342     * Sets the flag to display the dataset as an image.
343     *
344     * @param b
345     *            if b is true, display the dataset as an image
346     */
347    public final void setIsImageDisplay(boolean b) {
348        isImageDisplay = b;
349    }
350
351    /**
352     * Sets the flag to indicate this dataset is an image.
353     *
354     * @param b
355     *            if b is true, the dataset is an image.
356     */
357    public final void setIsImage(boolean b) {
358        isImage = b;
359    }
360
361    /**
362     * Sets data range for an image.
363     *
364     * @param min
365     *            the data range start.
366     * @param max
367     *            the data range end.
368     */
369    public final void setImageDataRange(double min, double max) {
370        if (max <= min)
371            return;
372
373        if (imageDataRange == null)
374            imageDataRange = new double[2];
375
376        imageDataRange[0] = min;
377        imageDataRange[1] = max;
378    }
379
380    /**
381     * Add a value that will be filtered out in an image.
382     *
383     * @param x
384     *            value to be filtered
385     */
386    public void addFilteredImageValue(Number x) {
387        Iterator<Number> it = filteredImageValues.iterator();
388        while (it.hasNext()) {
389            if (it.next().toString().equals(x.toString()))
390                return;
391        }
392
393        filteredImageValues.add(x);
394    }
395
396    /**
397     * Get a list of values that will be filtered out in an image.
398     *
399     * @return the list of Image values
400     */
401    public List<Number> getFilteredImageValues() {
402        return filteredImageValues;
403    }
404
405    /**
406     * @return true if this dataset is a true color image.
407     *
408     */
409
410    public final boolean isTrueColor() {
411        return isTrueColor;
412    }
413
414    /**
415     * Returns the interlace mode of a true color image (RGB).
416     *
417     * Valid values:
418     *
419     * <pre>
420     *     INTERLACE_PIXEL -- RGB components are contiguous, i.e. rgb, rgb, rgb, ...
421     *     INTERLACE_LINE -- each RGB component is stored as a scan line
422     *     INTERLACE_PLANE -- each RGB component is stored as a plane
423     * </pre>
424     *
425     * @return the interlace mode of a true color image (RGB).
426     */
427    public final int getInterlace() {
428        return interlace;
429    }
430
431    /**
432     * Returns the (min, max) pair of image data range.
433     *
434     * @return the (min, max) pair of image data range.
435     */
436    public double[] getImageDataRange() {
437        return imageDataRange;
438    }
439
440    /**
441     * Returns the fill values for the dataset.
442     *
443     * @return the fill values for the dataset.
444     */
445    @Override
446    public final Object getFillValue() {
447        return fillValue;
448    }
449}