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}