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}