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