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}