/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfTileOffsets.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /////////////////////////////////////////////////////////////////////////// |
2 | | // |
3 | | // Copyright (c) 2004, Industrial Light & Magic, a division of Lucas |
4 | | // Digital Ltd. LLC |
5 | | // |
6 | | // All rights reserved. |
7 | | // |
8 | | // Redistribution and use in source and binary forms, with or without |
9 | | // modification, are permitted provided that the following conditions are |
10 | | // met: |
11 | | // * Redistributions of source code must retain the above copyright |
12 | | // notice, this list of conditions and the following disclaimer. |
13 | | // * Redistributions in binary form must reproduce the above |
14 | | // copyright notice, this list of conditions and the following disclaimer |
15 | | // in the documentation and/or other materials provided with the |
16 | | // distribution. |
17 | | // * Neither the name of Industrial Light & Magic nor the names of |
18 | | // its contributors may be used to endorse or promote products derived |
19 | | // from this software without specific prior written permission. |
20 | | // |
21 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
24 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
25 | | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
26 | | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
27 | | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
31 | | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | | // |
33 | | /////////////////////////////////////////////////////////////////////////// |
34 | | |
35 | | |
36 | | //----------------------------------------------------------------------------- |
37 | | // |
38 | | // class TileOffsets |
39 | | // |
40 | | //----------------------------------------------------------------------------- |
41 | | |
42 | | #include <ImfTileOffsets.h> |
43 | | #include <ImfXdr.h> |
44 | | #include <ImfIO.h> |
45 | | #include "Iex.h" |
46 | | #include "ImfNamespace.h" |
47 | | #include <algorithm> |
48 | | |
49 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
50 | | |
51 | | |
52 | | TileOffsets::TileOffsets (LevelMode mode, |
53 | | int numXLevels, int numYLevels, |
54 | | const int *numXTiles, const int *numYTiles) |
55 | | : |
56 | | _mode (mode), |
57 | | _numXLevels (numXLevels), |
58 | | _numYLevels (numYLevels) |
59 | 0 | { |
60 | 0 | switch (_mode) |
61 | 0 | { |
62 | 0 | case ONE_LEVEL: |
63 | 0 | case MIPMAP_LEVELS: |
64 | |
|
65 | 0 | _offsets.resize (_numXLevels); |
66 | |
|
67 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
68 | 0 | { |
69 | 0 | _offsets[l].resize (numYTiles[l]); |
70 | |
|
71 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
72 | 0 | { |
73 | 0 | _offsets[l][dy].resize (numXTiles[l]); |
74 | 0 | } |
75 | 0 | } |
76 | 0 | break; |
77 | | |
78 | 0 | case RIPMAP_LEVELS: |
79 | |
|
80 | 0 | _offsets.resize (_numXLevels * _numYLevels); |
81 | |
|
82 | 0 | for (int ly = 0; ly < _numYLevels; ++ly) |
83 | 0 | { |
84 | 0 | for (int lx = 0; lx < _numXLevels; ++lx) |
85 | 0 | { |
86 | 0 | int l = ly * _numXLevels + lx; |
87 | 0 | _offsets[l].resize (numYTiles[ly]); |
88 | |
|
89 | 0 | for (size_t dy = 0; dy < _offsets[l].size(); ++dy) |
90 | 0 | { |
91 | 0 | _offsets[l][dy].resize (numXTiles[lx]); |
92 | 0 | } |
93 | 0 | } |
94 | 0 | } |
95 | 0 | break; |
96 | | |
97 | 0 | case NUM_LEVELMODES : |
98 | 0 | throw IEX_NAMESPACE::ArgExc("Bad initialisation of TileOffsets object"); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | |
103 | | bool |
104 | | TileOffsets::anyOffsetsAreInvalid () const |
105 | 0 | { |
106 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
107 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
108 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
109 | 0 | if (_offsets[l][dy][dx] <= 0) |
110 | 0 | return true; |
111 | | |
112 | 0 | return false; |
113 | 0 | } |
114 | | |
115 | | |
116 | | void |
117 | | TileOffsets::findTiles (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, bool isMultiPartFile, bool isDeep, bool skipOnly) |
118 | 0 | { |
119 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
120 | 0 | { |
121 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
122 | 0 | { |
123 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
124 | 0 | { |
125 | 0 | Int64 tileOffset = is.tellg(); |
126 | |
|
127 | 0 | if (isMultiPartFile) |
128 | 0 | { |
129 | 0 | int partNumber; |
130 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, partNumber); |
131 | 0 | } |
132 | |
|
133 | 0 | int tileX; |
134 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tileX); |
135 | |
|
136 | 0 | int tileY; |
137 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tileY); |
138 | |
|
139 | 0 | int levelX; |
140 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelX); |
141 | |
|
142 | 0 | int levelY; |
143 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelY); |
144 | |
|
145 | 0 | if(isDeep) |
146 | 0 | { |
147 | 0 | Int64 packed_offset_table_size; |
148 | 0 | Int64 packed_sample_size; |
149 | | |
150 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset_table_size); |
151 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_sample_size); |
152 | | |
153 | | // next Int64 is unpacked sample size - skip that too |
154 | 0 | Xdr::skip <StreamIO> (is, packed_offset_table_size+packed_sample_size+8); |
155 | | |
156 | 0 | }else{ |
157 | | |
158 | 0 | int dataSize; |
159 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, dataSize); |
160 | |
|
161 | 0 | Xdr::skip <StreamIO> (is, dataSize); |
162 | 0 | } |
163 | 0 | if (skipOnly) continue; |
164 | | |
165 | 0 | if (!isValidTile(tileX, tileY, levelX, levelY)) |
166 | 0 | return; |
167 | | |
168 | 0 | operator () (tileX, tileY, levelX, levelY) = tileOffset; |
169 | 0 | } |
170 | 0 | } |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | | |
175 | | void |
176 | | TileOffsets::reconstructFromFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,bool isMultiPart,bool isDeep) |
177 | 0 | { |
178 | | // |
179 | | // Try to reconstruct a missing tile offset table by sequentially |
180 | | // scanning through the file, and recording the offsets in the file |
181 | | // of the tiles we find. |
182 | | // |
183 | |
|
184 | 0 | Int64 position = is.tellg(); |
185 | |
|
186 | 0 | try |
187 | 0 | { |
188 | 0 | findTiles (is,isMultiPart,isDeep,false); |
189 | 0 | } |
190 | 0 | catch (...) |
191 | 0 | { |
192 | | // |
193 | | // Suppress all exceptions. This function is called only to |
194 | | // reconstruct the tile offset table for incomplete files, |
195 | | // and exceptions are likely. |
196 | | // |
197 | 0 | } |
198 | |
|
199 | 0 | is.clear(); |
200 | 0 | is.seekg (position); |
201 | 0 | } |
202 | | |
203 | | |
204 | | void |
205 | | TileOffsets::readFrom (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, bool &complete,bool isMultiPartFile, bool isDeep) |
206 | 0 | { |
207 | | // |
208 | | // Read in the tile offsets from the file's tile offset table |
209 | | // |
210 | |
|
211 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
212 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
213 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
214 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, _offsets[l][dy][dx]); |
215 | | |
216 | | // |
217 | | // Check if any tile offsets are invalid. |
218 | | // |
219 | | // Invalid offsets mean that the file is probably incomplete |
220 | | // (the offset table is the last thing written to the file). |
221 | | // Either some process is still busy writing the file, or |
222 | | // writing the file was aborted. |
223 | | // |
224 | | // We should still be able to read the existing parts of the |
225 | | // file. In order to do this, we have to make a sequential |
226 | | // scan over the scan tile to reconstruct the tile offset |
227 | | // table. |
228 | | // |
229 | |
|
230 | 0 | if (anyOffsetsAreInvalid()) |
231 | 0 | { |
232 | 0 | complete = false; |
233 | 0 | reconstructFromFile (is,isMultiPartFile,isDeep); |
234 | 0 | } |
235 | 0 | else |
236 | 0 | { |
237 | 0 | complete = true; |
238 | 0 | } |
239 | |
|
240 | 0 | } |
241 | | |
242 | | |
243 | | void |
244 | | TileOffsets::readFrom (std::vector<Int64> chunkOffsets,bool &complete) |
245 | 0 | { |
246 | 0 | size_t totalSize = 0; |
247 | | |
248 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
249 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
250 | 0 | totalSize += _offsets[l][dy].size(); |
251 | |
|
252 | 0 | if (chunkOffsets.size() != totalSize) |
253 | 0 | throw IEX_NAMESPACE::ArgExc ("Wrong offset count, not able to read from this array"); |
254 | | |
255 | | |
256 | | |
257 | 0 | int pos = 0; |
258 | 0 | for (size_t l = 0; l < _offsets.size(); ++l) |
259 | 0 | for (size_t dy = 0; dy < _offsets[l].size(); ++dy) |
260 | 0 | for (size_t dx = 0; dx < _offsets[l][dy].size(); ++dx) |
261 | 0 | { |
262 | 0 | _offsets[l][dy][dx] = chunkOffsets[pos]; |
263 | 0 | pos++; |
264 | 0 | } |
265 | |
|
266 | 0 | complete = !anyOffsetsAreInvalid(); |
267 | |
|
268 | 0 | } |
269 | | |
270 | | |
271 | | Int64 |
272 | | TileOffsets::writeTo (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os) const |
273 | 0 | { |
274 | | // |
275 | | // Write the tile offset table to the file, and |
276 | | // return the position of the start of the table |
277 | | // in the file. |
278 | | // |
279 | | |
280 | 0 | Int64 pos = os.tellp(); |
281 | |
|
282 | 0 | if (pos == -1) |
283 | 0 | IEX_NAMESPACE::throwErrnoExc ("Cannot determine current file position (%T)."); |
284 | |
|
285 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
286 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
287 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
288 | 0 | OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, _offsets[l][dy][dx]); |
289 | |
|
290 | 0 | return pos; |
291 | 0 | } |
292 | | |
293 | | namespace { |
294 | | struct tilepos{ |
295 | | Int64 filePos; |
296 | | int dx; |
297 | | int dy; |
298 | | int l; |
299 | | bool operator <(const tilepos & other) const |
300 | 0 | { |
301 | 0 | return filePos < other.filePos; |
302 | 0 | } |
303 | | }; |
304 | | } |
305 | | //------------------------------------- |
306 | | // fill array with tile coordinates in the order they appear in the file |
307 | | // |
308 | | // each input array must be of size (totalTiles) |
309 | | // |
310 | | // |
311 | | // if the tile order is not RANDOM_Y, it is more efficient to compute the |
312 | | // tile ordering rather than using this function |
313 | | // |
314 | | //------------------------------------- |
315 | | void TileOffsets::getTileOrder(int dx_table[],int dy_table[],int lx_table[],int ly_table[]) const |
316 | 0 | { |
317 | | // |
318 | | // helper class |
319 | | // |
320 | | |
321 | | // how many entries? |
322 | 0 | size_t entries=0; |
323 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
324 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
325 | 0 | entries+=_offsets[l][dy].size(); |
326 | | |
327 | 0 | std::vector<struct tilepos> table(entries); |
328 | | |
329 | 0 | size_t i = 0; |
330 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
331 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
332 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
333 | 0 | { |
334 | 0 | table[i].filePos = _offsets[l][dy][dx]; |
335 | 0 | table[i].dx = dx; |
336 | 0 | table[i].dy = dy; |
337 | 0 | table[i].l = l; |
338 | |
|
339 | 0 | ++i; |
340 | | |
341 | 0 | } |
342 | | |
343 | 0 | std::sort(table.begin(),table.end()); |
344 | | |
345 | | // |
346 | | // write out the values |
347 | | // |
348 | | |
349 | | // pass 1: write out dx and dy, since these are independent of level mode |
350 | | |
351 | 0 | for(size_t i=0;i<entries;i++) |
352 | 0 | { |
353 | 0 | dx_table[i] = table[i].dx; |
354 | 0 | dy_table[i] = table[i].dy; |
355 | 0 | } |
356 | | |
357 | | // now write out the levels, which depend on the level mode |
358 | | |
359 | 0 | switch (_mode) |
360 | 0 | { |
361 | 0 | case ONE_LEVEL: |
362 | 0 | { |
363 | 0 | for(size_t i=0;i<entries;i++) |
364 | 0 | { |
365 | 0 | lx_table[i] = 0; |
366 | 0 | ly_table[i] = 0; |
367 | 0 | } |
368 | 0 | break; |
369 | 0 | } |
370 | 0 | case MIPMAP_LEVELS: |
371 | 0 | { |
372 | 0 | for(size_t i=0;i<entries;i++) |
373 | 0 | { |
374 | 0 | lx_table[i]= table[i].l; |
375 | 0 | ly_table[i] =table[i].l; |
376 | | |
377 | 0 | } |
378 | 0 | break; |
379 | 0 | } |
380 | | |
381 | 0 | case RIPMAP_LEVELS: |
382 | 0 | { |
383 | 0 | for(size_t i=0;i<entries;i++) |
384 | 0 | { |
385 | 0 | lx_table[i]= table[i].l % _numXLevels; |
386 | 0 | ly_table[i] = table[i].l / _numXLevels; |
387 | | |
388 | 0 | } |
389 | 0 | break; |
390 | 0 | } |
391 | 0 | case NUM_LEVELMODES : |
392 | 0 | throw IEX_NAMESPACE::LogicExc("Bad level mode getting tile order"); |
393 | 0 | } |
394 | | |
395 | | |
396 | | |
397 | 0 | } |
398 | | |
399 | | |
400 | | bool |
401 | | TileOffsets::isEmpty () const |
402 | 0 | { |
403 | 0 | for (unsigned int l = 0; l < _offsets.size(); ++l) |
404 | 0 | for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy) |
405 | 0 | for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx) |
406 | 0 | if (_offsets[l][dy][dx] != 0) |
407 | 0 | return false; |
408 | 0 | return true; |
409 | 0 | } |
410 | | |
411 | | |
412 | | bool |
413 | | TileOffsets::isValidTile (int dx, int dy, int lx, int ly) const |
414 | 0 | { |
415 | 0 | if(lx<0 || ly < 0 || dx<0 || dy < 0) return false; |
416 | 0 | switch (_mode) |
417 | 0 | { |
418 | 0 | case ONE_LEVEL: |
419 | |
|
420 | 0 | if (lx == 0 && |
421 | 0 | ly == 0 && |
422 | 0 | _offsets.size() > 0 && |
423 | 0 | int(_offsets[0].size()) > dy && |
424 | 0 | int(_offsets[0][dy].size()) > dx) |
425 | 0 | { |
426 | 0 | return true; |
427 | 0 | } |
428 | | |
429 | 0 | break; |
430 | | |
431 | 0 | case MIPMAP_LEVELS: |
432 | |
|
433 | 0 | if (lx < _numXLevels && |
434 | 0 | ly < _numYLevels && |
435 | 0 | int(_offsets.size()) > lx && |
436 | 0 | int(_offsets[lx].size()) > dy && |
437 | 0 | int(_offsets[lx][dy].size()) > dx) |
438 | 0 | { |
439 | 0 | return true; |
440 | 0 | } |
441 | | |
442 | 0 | break; |
443 | | |
444 | 0 | case RIPMAP_LEVELS: |
445 | |
|
446 | 0 | if (lx < _numXLevels && |
447 | 0 | ly < _numYLevels && |
448 | 0 | (_offsets.size() > (size_t) lx+ ly * (size_t) _numXLevels) && |
449 | 0 | int(_offsets[lx + ly * _numXLevels].size()) > dy && |
450 | 0 | int(_offsets[lx + ly * _numXLevels][dy].size()) > dx) |
451 | 0 | { |
452 | 0 | return true; |
453 | 0 | } |
454 | | |
455 | 0 | break; |
456 | | |
457 | 0 | default: |
458 | |
|
459 | 0 | return false; |
460 | 0 | } |
461 | | |
462 | 0 | return false; |
463 | 0 | } |
464 | | |
465 | | |
466 | | Int64 & |
467 | | TileOffsets::operator () (int dx, int dy, int lx, int ly) |
468 | 0 | { |
469 | | // |
470 | | // Looks up the value of the tile with tile coordinate (dx, dy) |
471 | | // and level number (lx, ly) in the _offsets array, and returns |
472 | | // the cooresponding offset. |
473 | | // |
474 | |
|
475 | 0 | switch (_mode) |
476 | 0 | { |
477 | 0 | case ONE_LEVEL: |
478 | |
|
479 | 0 | return _offsets[0][dy][dx]; |
480 | 0 | break; |
481 | | |
482 | 0 | case MIPMAP_LEVELS: |
483 | |
|
484 | 0 | return _offsets[lx][dy][dx]; |
485 | 0 | break; |
486 | | |
487 | 0 | case RIPMAP_LEVELS: |
488 | |
|
489 | 0 | return _offsets[lx + ly * _numXLevels][dy][dx]; |
490 | 0 | break; |
491 | | |
492 | 0 | default: |
493 | |
|
494 | 0 | throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format."); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | |
499 | | Int64 & |
500 | | TileOffsets::operator () (int dx, int dy, int l) |
501 | 0 | { |
502 | 0 | return operator () (dx, dy, l, l); |
503 | 0 | } |
504 | | |
505 | | |
506 | | const Int64 & |
507 | | TileOffsets::operator () (int dx, int dy, int lx, int ly) const |
508 | 0 | { |
509 | | // |
510 | | // Looks up the value of the tile with tile coordinate (dx, dy) |
511 | | // and level number (lx, ly) in the _offsets array, and returns |
512 | | // the cooresponding offset. |
513 | | // |
514 | |
|
515 | 0 | switch (_mode) |
516 | 0 | { |
517 | 0 | case ONE_LEVEL: |
518 | |
|
519 | 0 | return _offsets[0][dy][dx]; |
520 | 0 | break; |
521 | | |
522 | 0 | case MIPMAP_LEVELS: |
523 | |
|
524 | 0 | return _offsets[lx][dy][dx]; |
525 | 0 | break; |
526 | | |
527 | 0 | case RIPMAP_LEVELS: |
528 | |
|
529 | 0 | return _offsets[lx + ly * _numXLevels][dy][dx]; |
530 | 0 | break; |
531 | | |
532 | 0 | default: |
533 | |
|
534 | 0 | throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format."); |
535 | 0 | } |
536 | 0 | } |
537 | | |
538 | | |
539 | | const Int64 & |
540 | | TileOffsets::operator () (int dx, int dy, int l) const |
541 | 0 | { |
542 | 0 | return operator () (dx, dy, l, l); |
543 | 0 | } |
544 | | |
545 | | const std::vector<std::vector<std::vector <Int64> > >& |
546 | | TileOffsets::getOffsets() const |
547 | 0 | { |
548 | 0 | return _offsets; |
549 | 0 | } |
550 | | |
551 | | |
552 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |