4 import java.lang.reflect.Array;
5 import java.io.IOException;
7 /** This class provides a subset of an N-dimensional image.
8 * Modified May 2, 2000 by T. McGlynn to permit
9 * tiles that go off the edge of the image.
11 public class ImageTiler {
19 * @param f The random access device from which image data may be read.
20 * This may be null if the tile information is available from
22 * @param fileOffset The file offset within the RandomAccess device at which
24 * @param dims The actual dimensions of the image.
25 * @param base The base class (should be a primitive type) of the image.
27 public ImageTiler(RandomAccess f, long fileOffset, int[] dims,
30 this.fileOffset = fileOffset;
35 /** See if we can get the image data from memory.
36 * This may be overriden by other classes, notably
37 * in nom.tam.fits.ImageData.
39 public Object getMemoryImage() {
43 /** Get a subset of the image. An image tile is returned
44 * as a one-dimensional array although the image will
45 * normally be multi-dimensional.
46 * @param The starting corner (using 0 as the start) for the image.
47 * @param The length requested in each dimension.
49 public Object getTile(int[] corners, int[] lengths) throws IOException {
51 if (corners.length != dims.length || lengths.length != dims.length) {
52 throw new IOException("Inconsistent sub-image request");
56 for (int i = 0; i < dims.length; i += 1) {
58 if (corners[i] < 0 || lengths[i] < 0 || corners[i] + lengths[i] > dims[i]) {
59 throw new IOException("Sub-image not within image");
62 arraySize *= lengths[i];
65 Object outArray = ArrayFuncs.newInstance(base, arraySize);
67 getTile(outArray, corners, lengths);
71 /** Get a tile, filling in a prespecified array.
72 * This version does not check that the user hase
73 * entered a valid set of corner and length arrays.
74 * ensure that out matches the
75 * length implied by the lengths array.
77 * @param outArray The output tile array. A one-dimensional
79 * Data not within the valid limits of the image will
80 * be left unchanged. The length of this
81 * array should be the product of lengths.
82 * @param corners The corners of the tile.
83 * @param lengths The dimensions of the tile.
86 public void getTile(Object outArray, int[] corners, int[] lengths)
89 Object data = getMemoryImage();
91 if (data == null && f == null) {
92 throw new IOException("No data source for tile subset");
94 fillTile(data, outArray, dims, corners, lengths);
98 * @param data The memory-resident data image.
99 * This may be null if the image is to
100 * be read from a file. This should
101 * be a multi-dimensional primitive array.
102 * @param o The tile to be filled. This is a
103 * simple primitive array.
104 * @param dims The dimensions of the full image.
105 * @param corners The indices of the corner of the image.
106 * @param lengths The dimensions of the subset.
108 protected void fillTile(Object data, Object o, int[] dims, int[] corners, int[] lengths)
113 int[] posits = new int[n];
114 int baseLength = ArrayFuncs.getBaseLength(o);
115 int segment = lengths[n - 1];
117 System.arraycopy(corners, 0, posits, 0, n);
118 long currentOffset = 0;
120 currentOffset = f.getFilePointer();
123 int outputOffset = 0;
128 // This implies there is some overlap
129 // in the last index (in conjunction
132 int mx = dims.length - 1;
133 boolean validSegment =
134 posits[mx] + lengths[mx] >= 0
135 && posits[mx] < dims[mx];
138 // Don't do anything for the current
139 // segment if anything but the
140 // last index is out of range.
143 for (int i = 0; i < mx; i += 1) {
144 if (posits[i] < 0 || posits[i] >= dims[i]) {
145 validSegment = false;
153 fillMemData(data, posits, segment, o, outputOffset, 0);
155 int offset = getOffset(dims, posits) * baseLength;
157 // Point to offset at real beginning
159 int actualLen = segment;
160 int actualOffset = offset;
161 int actualOutput = outputOffset;
162 if (posits[mx] < 0) {
163 actualOffset -= posits[mx] * baseLength;
164 actualOutput -= posits[mx];
165 actualLen += posits[mx];
167 if (posits[mx] + segment > dims[mx]) {
168 actualLen -= posits[mx] + segment - dims[mx];
170 fillFileData(o, actualOffset, actualOutput, actualLen);
173 outputOffset += segment;
175 } while (incrementPosition(corners, posits, lengths));
177 f.seek(currentOffset);
181 /** Fill a single segment from memory.
182 * This routine is called recursively to handle multi-dimensional
183 * arrays. E.g., if data is three-dimensional, this will
184 * recurse two levels until we get a call with a single dimensional
185 * datum. At that point the appropriate data will be copied
188 * @param data The in-memory image data.
189 * @param posits The current position for which data is requested.
190 * @param length The size of the segments.
191 * @param output The output tile.
192 * @param outputOffset The current offset into the output tile.
193 * @param dim The current dimension being
195 protected void fillMemData(Object data, int[] posits, int length,
196 Object output, int outputOffset, int dim) {
199 if (data instanceof Object[]) {
201 Object[] xo = (Object[]) data;
202 fillMemData(xo[posits[dim]], posits, length, output, outputOffset, dim + 1);
206 // Adjust the spacing for the actual copy.
207 int startFrom = posits[dim];
208 int startTo = outputOffset;
209 int copyLength = length;
211 if (posits[dim] < 0) {
212 startFrom -= posits[dim];
213 startTo -= posits[dim];
214 copyLength += posits[dim];
216 if (posits[dim] + length > dims[dim]) {
217 copyLength -= (posits[dim] + length - dims[dim]);
220 System.arraycopy(data, startFrom, output, startTo, copyLength);
224 /** File a tile segment from a file.
225 * @param output The output tile.
226 * @param delta The offset from the beginning of the image in bytes.
227 * @param outputOffset The index into the output array.
228 * @param segment The number of elements to be read for this segment.
230 protected void fillFileData(Object output, int delta, int outputOffset,
231 int segment) throws IOException {
234 f.seek(fileOffset + delta);
236 if (base == float.class) {
237 f.read((float[]) output, outputOffset, segment);
238 } else if (base == int.class) {
239 f.read((int[]) output, outputOffset, segment);
240 } else if (base == short.class) {
241 f.read((short[]) output, outputOffset, segment);
242 } else if (base == double.class) {
243 f.read((double[]) output, outputOffset, segment);
244 } else if (base == byte.class) {
245 f.read((byte[]) output, outputOffset, segment);
246 } else if (base == char.class) {
247 f.read((char[]) output, outputOffset, segment);
248 } else if (base == long.class) {
249 f.read((long[]) output, outputOffset, segment);
251 throw new IOException("Invalid type for tile array");
255 /** Increment the offset within the position array.
256 * Note that we never look at the last index since
257 * we copy data a block at a time and not byte by byte.
258 * @param start The starting corner values.
259 * @param current The current offsets.
260 * @param lengths The desired dimensions of the subset.
262 protected static boolean incrementPosition(int[] start,
266 for (int i = start.length - 2; i >= 0; i -= 1) {
267 if (current[i] - start[i] < lengths[i] - 1) {
269 for (int j = i + 1; j < start.length - 1; j += 1) {
270 current[j] = start[j];
278 /** Get the offset of a given position.
279 * @param dims The dimensions of the array.
280 * @param pos The index requested.
282 public static final int getOffset(int[] dims, int[] pos) {
285 for (int i = 0; i < dims.length; i += 1) {
294 /** Read the entire image into a multidimensional
297 public Object getCompleteImage() throws IOException {
300 throw new IOException("Attempt to read from null file");
302 long currentOffset = f.getFilePointer();
303 Object o = ArrayFuncs.newInstance(base, dims);
306 f.seek(currentOffset);