1 package nom.tam.image.tile.operation; 2 3 /* 4 * #%L 5 * nom.tam FITS library 6 * %% 7 * Copyright (C) 1996 - 2024 nom-tam-fits 8 * %% 9 * This is free and unencumbered software released into the public domain. 10 * 11 * Anyone is free to copy, modify, publish, use, compile, sell, or 12 * distribute this software, either in source code form or as a compiled 13 * binary, for any purpose, commercial or non-commercial, and by any 14 * means. 15 * 16 * In jurisdictions that recognize copyright laws, the author or authors 17 * of this software dedicate any and all copyright interest in the 18 * software to the public domain. We make this dedication for the benefit 19 * of the public at large and to the detriment of our heirs and 20 * successors. We intend this dedication to be an overt act of 21 * relinquishment in perpetuity of all present and future rights to this 22 * software under copyright law. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 27 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 28 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 29 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 30 * OTHER DEALINGS IN THE SOFTWARE. 31 * #L% 32 */ 33 34 import java.lang.reflect.Array; 35 import java.nio.Buffer; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 39 import nom.tam.fits.FitsException; 40 import nom.tam.util.ArrayFuncs; 41 import nom.tam.util.type.ElementType; 42 43 /** 44 * A base implementation of 2D image tile compression. 45 * 46 * @param <OPERATION> The generic type of tile operation that handles parallel processing 47 */ 48 public abstract class AbstractTiledImageOperation<OPERATION extends ITileOperation> implements ITiledImageOperation { 49 50 /** Image axes in Java array index order (is is last!). */ 51 private int[] axes; 52 53 /** 54 * Interprets the value of the BITPIX keyword in the uncompressed FITS image 55 */ 56 private ElementType<Buffer> baseType; 57 58 /** Tile dimensions in Java array index order (x is last!) */ 59 private int[] tileAxes; 60 61 private OPERATION[] tileOperations; 62 63 private final Class<OPERATION> operationClass; 64 65 /** 66 * Creates a new tiling foperation. 67 * 68 * @param operationClass the class of tile operation. 69 * 70 * @deprecated (<i>for internal use</i>) This constructor should have protected visibility. 71 */ 72 public AbstractTiledImageOperation(Class<OPERATION> operationClass) { 73 this.operationClass = operationClass; 74 } 75 76 @Override 77 public ElementType<Buffer> getBaseType() { 78 return baseType; 79 } 80 81 /** 82 * Returns the number of elements that a buffer must have to store the entire image. 83 * 84 * @return The number of points in the full image. 85 */ 86 public int getBufferSize() { 87 int bufferSize = 1; 88 for (int axisValue : axes) { 89 bufferSize *= axisValue; 90 } 91 return bufferSize; 92 } 93 94 @Override 95 public int getImageWidth() { 96 return axes[axes.length - 1]; 97 } 98 99 @Override 100 public OPERATION getTileOperation(int i) { 101 return tileOperations[i]; 102 } 103 104 /** 105 * Sets the image dimensions, in Java array index order. 106 * 107 * @param axes Image dimensions in Java array index order (x is last!). 108 */ 109 public void setAxes(int[] axes) { 110 this.axes = Arrays.copyOf(axes, axes.length); 111 } 112 113 /** 114 * <p> 115 * Sets the tile dimension. Here the dimensions are in Java array index order, that is the x-dimension (width of 116 * tile) is last! 117 * </p> 118 * <p> 119 * Note, that because tile compression is essentially 2D, the tile sizes in higher dimensions will be forced to 1, 120 * even if specified otherwise by the argument (see 121 * <a href="https://heasarc.gsfc.nasa.gov/docs/software/fitsio/compression.html">FITSIO convention</a>). 122 * </p> 123 * 124 * @param value The tile dimensions in Java array index order (x is last!). Only up to the last 2 125 * components are considered. The rest will be assumed to have values equals to 1. 126 * 127 * @throws FitsException If the leading dimensions (before the last 2) have sizes not equal to 1 128 */ 129 public void setTileAxes(int[] value) throws FitsException { 130 for (int i = value.length - 2; --i >= 0;) { 131 if (value[i] != 1) { 132 throw new FitsException("Tile sizes in higher dimensions (>2) must be 1 as per the FITSIO convention (" + i 133 + ":" + value[i] + ")"); 134 } 135 } 136 tileAxes = Arrays.copyOf(value, value.length); 137 } 138 139 /** 140 * Checks if the image size has been defined. 141 * 142 * @return <code>true</code> if the size of the image to be tiled has been set, otherwise <code>false</code>. 143 */ 144 protected boolean hasAxes() { 145 return axes != null; 146 } 147 148 /** 149 * Checks if the tiling has been defined and tile sizes are set. 150 * 151 * @return <code>true</code> if the tile sizes have been defined already, otherwise <code>false</code> 152 */ 153 protected boolean hasTileAxes() { 154 return tileAxes != null; 155 } 156 157 private int getBufferOffset(int[] index) { 158 int l = 0; 159 int blockSize = 1; 160 for (int i = index.length; --i >= 0;) { 161 l += index[i] * blockSize; 162 blockSize *= axes[i]; 163 } 164 return l; 165 } 166 167 /** 168 * Creates a tiling pattern for an image. 169 * 170 * @param init the parameters that determine the tiling pattern 171 * 172 * @throws FitsException if the parameters are invalid. 173 */ 174 @SuppressWarnings("unchecked") 175 protected void createTiles(ITileOperationInitialisation<OPERATION> init) throws FitsException { 176 int[] offset = new int[axes.length]; // Tile start in image (Java index order) 177 int[] tileSize = new int[2]; // {w, h} 178 int pos = 0; 179 180 int imLength = 1; 181 for (int i = axes.length; --i >= 0;) { 182 imLength *= axes[i]; 183 } 184 185 tileSize[1] = 1; 186 187 // If tile is not defined along all axes, pad with 1. 188 if (tileAxes.length < axes.length) { 189 int[] tile = new int[axes.length]; 190 System.arraycopy(tileAxes, 0, tile, tile.length - tileAxes.length, tileAxes.length); 191 Arrays.fill(tile, 0, tile.length - tileAxes.length, 1); 192 tileAxes = tile; 193 } 194 195 ArrayList<OPERATION> opList = new ArrayList<>(); 196 197 // Create 2D tiles to cover image (in N dimensions, where N need not be 2) 198 for (int tileIndex = 0; pos < imLength; tileIndex++) { 199 // Calculate the actual size of the current tile 200 for (int i = Math.min(tileSize.length, tileAxes.length); --i >= 0;) { 201 int k = axes.length - 1 - i; 202 tileSize[i] = (offset[k] + tileAxes[k] > axes[k]) ? axes[k] - offset[k] : tileAxes[k]; 203 } 204 205 // Create the tile at the current buffer offset and tile size 206 OPERATION op = init.createTileOperation(tileIndex, new TileArea().start(ArrayFuncs.getReversed(offset))); 207 op.setDimensions(pos, tileSize[0], tileSize[1]); 208 opList.add(op); 209 210 // Calculate the image indices where the next tile starts. 211 for (int k = axes.length; --k >= 0;) { 212 offset[k] += tileAxes[k]; // Try next tile along the current dimension... 213 if (offset[k] < axes[k]) { 214 break; // OK, tile is within image bounds 215 } 216 if (k > 0) { 217 offset[k] = 0; // Otherwise reset the tile pos in the subarray dimensions 218 } 219 } 220 221 // Calculate the buffer position where the next tile starts. 222 pos = getBufferOffset(offset); 223 } 224 225 tileOperations = (OPERATION[]) Array.newInstance(operationClass, opList.size()); 226 opList.toArray(tileOperations); 227 228 init.tileCount(tileOperations.length); 229 230 for (OPERATION op : tileOperations) { 231 init.init(op); 232 } 233 } 234 235 /** 236 * Returns the dimensionality of the image. 237 * 238 * @return the dimensinality of the image, that is the number of cartesian axes it contains. 239 */ 240 protected int getNAxes() { 241 return axes.length; 242 } 243 244 /** 245 * Returns the number of tile operations that are needed to cover a tiled image. 246 * 247 * @return the number of tiles in the image. 248 */ 249 protected int getNumberOfTileOperations() { 250 return tileOperations.length; 251 } 252 253 /** 254 * Returns the reference to the tile dimensions array. The dimensions are stored in Java array index order, i.e., 255 * the x-dimension (width) is last. 256 * 257 * @return The tile dimensions in Java array index order (x is last!). 258 */ 259 protected int[] getTileAxes() { 260 return tileAxes; 261 } 262 263 /** 264 * Returns an array of parallel tile oprations, which cover the full image. 265 * 266 * @return an array of parallel tile operations. 267 */ 268 protected OPERATION[] getTileOperations() { 269 return tileOperations; 270 } 271 272 /** 273 * Sets the FITS element type that is contained in the tiles for this operation. 274 * 275 * @param baseType the FITS element type of data in the tile. 276 */ 277 protected void setBaseType(ElementType<Buffer> baseType) { 278 this.baseType = baseType; 279 } 280 }