1 package nom.tam.image;
2
3 import java.io.EOFException;
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 import java.io.IOException;
37 import java.lang.reflect.Array;
38 import java.util.Arrays;
39
40 import nom.tam.util.ArrayDataOutput;
41 import nom.tam.util.ArrayFuncs;
42 import nom.tam.util.RandomAccess;
43 import nom.tam.util.type.ElementType;
44
45 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
46
47
48
49
50
51
52
53
54
55
56 public abstract class StandardImageTiler implements ImageTiler {
57
58
59
60
61
62
63 public static long getOffset(int[] dims, int[] pos) {
64
65 long offset = 0;
66 for (int i = 0; i < dims.length; i++) {
67 if (i > 0) {
68 offset *= dims[i];
69 }
70 offset += pos[i];
71 }
72 return offset;
73 }
74
75
76
77
78
79
80
81
82
83
84
85 protected static boolean incrementPosition(int[] start, int[] current, int[] lengths) {
86 final int[] steps = new int[start.length];
87 Arrays.fill(steps, 1);
88 return StandardImageTiler.incrementPosition(start, current, lengths, steps);
89 }
90
91
92
93
94
95
96
97
98
99
100
101
102 protected static boolean incrementPosition(int[] start, int[] current, int[] lengths, int[] steps) {
103 for (int i = start.length - 2; i >= 0; i--) {
104 if (current[i] - start[i] < lengths[i] - steps[i]) {
105 current[i] += steps[i];
106 if (start.length - 1 - (i + 1) >= 0) {
107 System.arraycopy(start, i + 1, current, i + 1, start.length - 1 - (i + 1));
108 }
109 return true;
110 }
111 }
112 return false;
113 }
114
115 private final RandomAccess randomAccessFile;
116
117 private final long fileOffset;
118
119 private final int[] dims;
120
121 private final Class<?> base;
122
123
124
125
126
127
128
129
130
131
132 @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intended exposure of mutable data")
133 public StandardImageTiler(RandomAccess f, long fileOffset, int[] dims, Class<?> base) {
134 randomAccessFile = f;
135 this.fileOffset = fileOffset;
136 this.dims = dims;
137 this.base = base;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151 @SuppressFBWarnings(value = "RR_NOT_CHECKED", justification = "this read will never return less than the requested length")
152 protected void fillFileData(Object output, long delta, int outputOffset, int segment) throws IOException {
153 fillFileData(output, delta, outputOffset, segment, 1);
154 }
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 protected void fillFileData(Object output, long delta, int outputOffset, int segment, int step) throws IOException {
170 if (output instanceof ArrayDataOutput) {
171 this.fillFileData((ArrayDataOutput) output, delta, segment, step);
172 } else {
173 randomAccessFile.seek(fileOffset + delta);
174 int got = 0;
175
176 if (base == float.class) {
177 got = randomAccessFile.read((float[]) output, outputOffset, segment);
178 } else if (base == int.class) {
179 got = randomAccessFile.read((int[]) output, outputOffset, segment);
180 } else if (base == short.class) {
181 got = randomAccessFile.read((short[]) output, outputOffset, segment);
182 } else if (base == double.class) {
183 got = randomAccessFile.read((double[]) output, outputOffset, segment);
184 } else if (base == byte.class) {
185 got = randomAccessFile.read((byte[]) output, outputOffset, segment);
186 } else if (base == long.class) {
187 got = randomAccessFile.read((long[]) output, outputOffset, segment);
188 } else {
189 throw new IOException("Invalid type for tile array");
190 }
191
192 if (got < 0) {
193 throw new EOFException();
194 }
195 }
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209 @SuppressFBWarnings(value = "RR_NOT_CHECKED", justification = "this read will never return less than the requested length")
210 protected void fillFileData(ArrayDataOutput output, long delta, int segment) throws IOException {
211 fillFileData(output, delta, segment, 1);
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228 @SuppressFBWarnings(value = "RR_NOT_CHECKED", justification = "this read will never return less than the requested length")
229 protected void fillFileData(ArrayDataOutput output, long delta, int segment, int step) throws IOException {
230 final int byteSize = ElementType.forClass(base).size();
231
232
233
234
235 final int stepSize = (step - 1) * byteSize;
236 randomAccessFile.seek(fileOffset + delta);
237
238
239 final byte[] buffer = new byte[byteSize];
240 long seekOffset = randomAccessFile.position();
241 int bytesRead = 0;
242
243
244 final int expectedBytes = segment * byteSize;
245 while (bytesRead < expectedBytes) {
246
247 randomAccessFile.seek(seekOffset);
248 final int currReadByteCount = randomAccessFile.read(buffer, 0, buffer.length);
249
250
251 if (currReadByteCount < 0) {
252 break;
253 }
254 output.write(buffer, 0, currReadByteCount);
255 seekOffset = randomAccessFile.position() + stepSize;
256 bytesRead += currReadByteCount + stepSize;
257 }
258
259 output.flush();
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276 protected void fillMemData(Object data, int[] posits, int length, Object output, int outputOffset, int dim)
277 throws IOException {
278 fillMemData(data, posits, length, output, outputOffset, dim, 1);
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298 protected void fillMemData(Object data, int[] posits, int length, Object output, int outputOffset, int dim, int step)
299 throws IOException {
300
301 if (data instanceof Object[]) {
302
303 Object[] xo = (Object[]) data;
304 fillMemData(xo[posits[dim]], posits, length, output, outputOffset, dim + 1, step);
305
306 } else {
307
308
309 int startFrom = posits[dim];
310 int startTo = outputOffset;
311 int copyLength = length;
312
313 if (posits[dim] < 0) {
314 startFrom -= posits[dim];
315 startTo -= posits[dim];
316 copyLength += posits[dim];
317 }
318 if (posits[dim] + length > dims[dim]) {
319 copyLength -= posits[dim] + length - dims[dim];
320 }
321
322 if (output instanceof ArrayDataOutput) {
323
324
325 final ArrayDataOutput arrayDataOutput = ((ArrayDataOutput) output);
326 for (int i = startFrom; i < startFrom + copyLength; i += step) {
327 if (base == float.class) {
328 arrayDataOutput.writeFloat(Array.getFloat(data, i));
329 } else if (base == int.class) {
330 arrayDataOutput.writeInt(Array.getInt(data, i));
331 } else if (base == double.class) {
332 arrayDataOutput.writeDouble(Array.getDouble(data, i));
333 } else if (base == long.class) {
334 arrayDataOutput.writeLong(Array.getLong(data, i));
335 } else if (base == short.class) {
336 arrayDataOutput.writeShort(Array.getShort(data, i));
337 } else if (base == byte.class) {
338 arrayDataOutput.writeByte(Array.getByte(data, i));
339 }
340 }
341
342 arrayDataOutput.flush();
343 } else {
344 ArrayFuncs.copy(data, startFrom, output, startTo, copyLength, step);
345 }
346 }
347 }
348
349
350
351
352
353
354
355
356
357
358
359
360
361 protected void fillTile(Object data, Object o, int[] newDims, int[] corners, int[] lengths) throws IOException {
362 final int[] steps = new int[corners.length];
363 Arrays.fill(steps, 1);
364 fillTile(data, o, newDims, corners, lengths, steps);
365 }
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380 protected void fillTile(Object data, Object o, int[] newDims, int[] corners, int[] lengths, int[] steps)
381 throws IOException {
382
383 int n = newDims.length;
384 int[] posits = new int[n];
385 final boolean isStreaming = (o instanceof ArrayDataOutput);
386
387
388
389
390
391
392 final int baseLength = isStreaming ? ElementType.forClass(base).size() : ArrayFuncs.getBaseLength(o);
393
394 int segment = lengths[n - 1];
395 int segmentStep = steps[n - 1];
396
397 System.arraycopy(corners, 0, posits, 0, n);
398 long currentOffset = 0;
399 if (data == null) {
400 currentOffset = randomAccessFile.getFilePointer();
401 }
402
403 int outputOffset = 0;
404
405
406
407 boolean hasNoOverlap = true;
408
409 do {
410
411
412
413
414
415 int mx = newDims.length - 1;
416 boolean validSegment = posits[mx] + lengths[mx] >= 0 && posits[mx] < newDims[mx];
417
418
419
420
421
422 if (validSegment) {
423 for (int i = 0; i < mx; i++) {
424 if (posits[i] < 0 || posits[i] >= newDims[i]) {
425 validSegment = false;
426 break;
427 }
428 }
429 }
430
431 if (validSegment) {
432 hasNoOverlap = false;
433 if (data != null) {
434 fillMemData(data, posits, segment, o, outputOffset, 0, segmentStep);
435 } else {
436 long offset = getOffset(newDims, posits) * baseLength;
437
438
439
440 int actualLen = segment;
441 long actualOffset = offset;
442 int actualOutput = outputOffset;
443 if (posits[mx] < 0) {
444 actualOffset -= (long) posits[mx] * baseLength;
445 actualOutput -= posits[mx];
446 actualLen += posits[mx];
447 }
448 if (posits[mx] + segment > newDims[mx]) {
449 actualLen -= posits[mx] + segment - newDims[mx];
450 }
451 fillFileData(o, actualOffset, actualOutput, actualLen, segmentStep);
452 }
453 }
454 if (!isStreaming) {
455 outputOffset += segment;
456 }
457
458 } while (incrementPosition(corners, posits, lengths, steps));
459 if (data == null) {
460 randomAccessFile.seek(currentOffset);
461 }
462
463 if (isStreaming && hasNoOverlap) {
464 throw new IOException("Sub-image not within image");
465 }
466 }
467
468 @Override
469 public Object getCompleteImage() throws IOException {
470
471 if (randomAccessFile == null) {
472 throw new IOException("Attempt to read from null file");
473 }
474 long currentOffset = randomAccessFile.getFilePointer();
475 Object o = ArrayFuncs.newInstance(base, dims);
476 randomAccessFile.seek(fileOffset);
477 randomAccessFile.readImage(o);
478 randomAccessFile.seek(currentOffset);
479 return o;
480 }
481
482
483
484
485
486
487
488 protected abstract Object getMemoryImage();
489
490 @Override
491 public Object getTile(int[] corners, int[] lengths) throws IOException {
492 final int[] steps = new int[corners.length];
493 Arrays.fill(steps, 1);
494 return getTile(corners, lengths, steps);
495 }
496
497 @Override
498 public Object getTile(int[] corners, int[] lengths, int[] steps) throws IOException {
499
500 if (corners.length != dims.length || lengths.length != dims.length) {
501 throw new IOException("Inconsistent sub-image request");
502 }
503
504 int arraySize = 1;
505 for (int i = 0; i < dims.length; i++) {
506
507 if (corners[i] < 0 || lengths[i] < 0 || corners[i] + lengths[i] > dims[i]) {
508 throw new IOException("Sub-image not within image");
509 }
510 if (steps[i] < 1) {
511 throw new IOException("Step value cannot be less than 1.");
512 }
513
514 arraySize *= lengths[i];
515 }
516
517 Object outArray = ArrayFuncs.newInstance(base, arraySize);
518
519 getTile(outArray, corners, lengths, steps);
520 return outArray;
521 }
522
523 @Override
524 public void getTile(Object output, int[] corners, int[] lengths) throws IOException {
525 final int[] steps = new int[corners.length];
526 Arrays.fill(steps, 1);
527 this.getTile(output, corners, lengths, steps);
528 }
529
530 @Override
531 public void getTile(Object output, int[] corners, int[] lengths, int[] steps) throws IOException {
532 Object data = getMemoryImage();
533
534 if (data == null && randomAccessFile == null) {
535 throw new IOException("No data source for tile subset");
536 }
537
538 fillTile(data, output, dims, corners, lengths, steps);
539 }
540 }