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