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