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
TileOffsets(LevelMode mode,int numXLevels,int numYLevels,const int * numXTiles,const int * numYTiles)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 {
60 switch (_mode)
61 {
62 case ONE_LEVEL:
63 case MIPMAP_LEVELS:
64
65 _offsets.resize (_numXLevels);
66
67 for (unsigned int l = 0; l < _offsets.size(); ++l)
68 {
69 _offsets[l].resize (numYTiles[l]);
70
71 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
72 {
73 _offsets[l][dy].resize (numXTiles[l]);
74 }
75 }
76 break;
77
78 case RIPMAP_LEVELS:
79
80 _offsets.resize (_numXLevels * _numYLevels);
81
82 for (int ly = 0; ly < _numYLevels; ++ly)
83 {
84 for (int lx = 0; lx < _numXLevels; ++lx)
85 {
86 int l = ly * _numXLevels + lx;
87 _offsets[l].resize (numYTiles[ly]);
88
89 for (size_t dy = 0; dy < _offsets[l].size(); ++dy)
90 {
91 _offsets[l][dy].resize (numXTiles[lx]);
92 }
93 }
94 }
95 break;
96
97 case NUM_LEVELMODES :
98 throw IEX_NAMESPACE::ArgExc("Bad initialisation of TileOffsets object");
99 }
100 }
101
102
103 bool
anyOffsetsAreInvalid() const104 TileOffsets::anyOffsetsAreInvalid () const
105 {
106 for (unsigned int l = 0; l < _offsets.size(); ++l)
107 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
108 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
109 if (_offsets[l][dy][dx] <= 0)
110 return true;
111
112 return false;
113 }
114
115
116 void
findTiles(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,bool isMultiPartFile,bool isDeep,bool skipOnly)117 TileOffsets::findTiles (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, bool isMultiPartFile, bool isDeep, bool skipOnly)
118 {
119 for (unsigned int l = 0; l < _offsets.size(); ++l)
120 {
121 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
122 {
123 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
124 {
125 Int64 tileOffset = is.tellg();
126
127 if (isMultiPartFile)
128 {
129 int partNumber;
130 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, partNumber);
131 }
132
133 int tileX;
134 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tileX);
135
136 int tileY;
137 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, tileY);
138
139 int levelX;
140 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelX);
141
142 int levelY;
143 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, levelY);
144
145 if(isDeep)
146 {
147 Int64 packed_offset_table_size;
148 Int64 packed_sample_size;
149
150 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, packed_offset_table_size);
151 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 Xdr::skip <StreamIO> (is, packed_offset_table_size+packed_sample_size+8);
155
156 }else{
157
158 int dataSize;
159 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, dataSize);
160
161 Xdr::skip <StreamIO> (is, dataSize);
162 }
163 if (skipOnly) continue;
164
165 if (!isValidTile(tileX, tileY, levelX, levelY))
166 return;
167
168 operator () (tileX, tileY, levelX, levelY) = tileOffset;
169 }
170 }
171 }
172 }
173
174
175 void
reconstructFromFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,bool isMultiPart,bool isDeep)176 TileOffsets::reconstructFromFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,bool isMultiPart,bool isDeep)
177 {
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 Int64 position = is.tellg();
185
186 try
187 {
188 findTiles (is,isMultiPart,isDeep,false);
189 }
190 catch (...)
191 {
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 }
198
199 is.clear();
200 is.seekg (position);
201 }
202
203
204 void
readFrom(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,bool & complete,bool isMultiPartFile,bool isDeep)205 TileOffsets::readFrom (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, bool &complete,bool isMultiPartFile, bool isDeep)
206 {
207 //
208 // Read in the tile offsets from the file's tile offset table
209 //
210
211 for (unsigned int l = 0; l < _offsets.size(); ++l)
212 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
213 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
214 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 if (anyOffsetsAreInvalid())
231 {
232 complete = false;
233 reconstructFromFile (is,isMultiPartFile,isDeep);
234 }
235 else
236 {
237 complete = true;
238 }
239
240 }
241
242
243 void
readFrom(std::vector<Int64> chunkOffsets,bool & complete)244 TileOffsets::readFrom (std::vector<Int64> chunkOffsets,bool &complete)
245 {
246 size_t totalSize = 0;
247
248 for (unsigned int l = 0; l < _offsets.size(); ++l)
249 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
250 totalSize += _offsets[l][dy].size();
251
252 if (chunkOffsets.size() != totalSize)
253 throw IEX_NAMESPACE::ArgExc ("Wrong offset count, not able to read from this array");
254
255
256
257 int pos = 0;
258 for (size_t l = 0; l < _offsets.size(); ++l)
259 for (size_t dy = 0; dy < _offsets[l].size(); ++dy)
260 for (size_t dx = 0; dx < _offsets[l][dy].size(); ++dx)
261 {
262 _offsets[l][dy][dx] = chunkOffsets[pos];
263 pos++;
264 }
265
266 complete = !anyOffsetsAreInvalid();
267
268 }
269
270
271 Int64
writeTo(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os) const272 TileOffsets::writeTo (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os) const
273 {
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 Int64 pos = os.tellp();
281
282 if (pos == -1)
283 IEX_NAMESPACE::throwErrnoExc ("Cannot determine current file position (%T).");
284
285 for (unsigned int l = 0; l < _offsets.size(); ++l)
286 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
287 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
288 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::write <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (os, _offsets[l][dy][dx]);
289
290 return pos;
291 }
292
293 namespace {
294 struct tilepos{
295 Int64 filePos;
296 int dx;
297 int dy;
298 int l;
operator <__anonbd53aae40111::tilepos299 bool operator <(const tilepos & other) const
300 {
301 return filePos < other.filePos;
302 }
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 //-------------------------------------
getTileOrder(int dx_table[],int dy_table[],int lx_table[],int ly_table[]) const315 void TileOffsets::getTileOrder(int dx_table[],int dy_table[],int lx_table[],int ly_table[]) const
316 {
317 //
318 // helper class
319 //
320
321 // how many entries?
322 size_t entries=0;
323 for (unsigned int l = 0; l < _offsets.size(); ++l)
324 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
325 entries+=_offsets[l][dy].size();
326
327 std::vector<struct tilepos> table(entries);
328
329 size_t i = 0;
330 for (unsigned int l = 0; l < _offsets.size(); ++l)
331 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
332 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
333 {
334 table[i].filePos = _offsets[l][dy][dx];
335 table[i].dx = dx;
336 table[i].dy = dy;
337 table[i].l = l;
338
339 ++i;
340
341 }
342
343 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 for(size_t i=0;i<entries;i++)
352 {
353 dx_table[i] = table[i].dx;
354 dy_table[i] = table[i].dy;
355 }
356
357 // now write out the levels, which depend on the level mode
358
359 switch (_mode)
360 {
361 case ONE_LEVEL:
362 {
363 for(size_t i=0;i<entries;i++)
364 {
365 lx_table[i] = 0;
366 ly_table[i] = 0;
367 }
368 break;
369 }
370 case MIPMAP_LEVELS:
371 {
372 for(size_t i=0;i<entries;i++)
373 {
374 lx_table[i]= table[i].l;
375 ly_table[i] =table[i].l;
376
377 }
378 break;
379 }
380
381 case RIPMAP_LEVELS:
382 {
383 for(size_t i=0;i<entries;i++)
384 {
385 lx_table[i]= table[i].l % _numXLevels;
386 ly_table[i] = table[i].l / _numXLevels;
387
388 }
389 break;
390 }
391 case NUM_LEVELMODES :
392 throw IEX_NAMESPACE::LogicExc("Bad level mode getting tile order");
393 }
394
395
396
397 }
398
399
400 bool
isEmpty() const401 TileOffsets::isEmpty () const
402 {
403 for (unsigned int l = 0; l < _offsets.size(); ++l)
404 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
405 for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
406 if (_offsets[l][dy][dx] != 0)
407 return false;
408 return true;
409 }
410
411
412 bool
isValidTile(int dx,int dy,int lx,int ly) const413 TileOffsets::isValidTile (int dx, int dy, int lx, int ly) const
414 {
415 if(lx<0 || ly < 0 || dx<0 || dy < 0) return false;
416 switch (_mode)
417 {
418 case ONE_LEVEL:
419
420 if (lx == 0 &&
421 ly == 0 &&
422 _offsets.size() > 0 &&
423 int(_offsets[0].size()) > dy &&
424 int(_offsets[0][dy].size()) > dx)
425 {
426 return true;
427 }
428
429 break;
430
431 case MIPMAP_LEVELS:
432
433 if (lx < _numXLevels &&
434 ly < _numYLevels &&
435 int(_offsets.size()) > lx &&
436 int(_offsets[lx].size()) > dy &&
437 int(_offsets[lx][dy].size()) > dx)
438 {
439 return true;
440 }
441
442 break;
443
444 case RIPMAP_LEVELS:
445
446 if (lx < _numXLevels &&
447 ly < _numYLevels &&
448 (_offsets.size() > (size_t) lx+ ly * (size_t) _numXLevels) &&
449 int(_offsets[lx + ly * _numXLevels].size()) > dy &&
450 int(_offsets[lx + ly * _numXLevels][dy].size()) > dx)
451 {
452 return true;
453 }
454
455 break;
456
457 default:
458
459 return false;
460 }
461
462 return false;
463 }
464
465
466 Int64 &
operator ()(int dx,int dy,int lx,int ly)467 TileOffsets::operator () (int dx, int dy, int lx, int ly)
468 {
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 switch (_mode)
476 {
477 case ONE_LEVEL:
478
479 return _offsets[0][dy][dx];
480 break;
481
482 case MIPMAP_LEVELS:
483
484 return _offsets[lx][dy][dx];
485 break;
486
487 case RIPMAP_LEVELS:
488
489 return _offsets[lx + ly * _numXLevels][dy][dx];
490 break;
491
492 default:
493
494 throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
495 }
496 }
497
498
499 Int64 &
operator ()(int dx,int dy,int l)500 TileOffsets::operator () (int dx, int dy, int l)
501 {
502 return operator () (dx, dy, l, l);
503 }
504
505
506 const Int64 &
operator ()(int dx,int dy,int lx,int ly) const507 TileOffsets::operator () (int dx, int dy, int lx, int ly) const
508 {
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 switch (_mode)
516 {
517 case ONE_LEVEL:
518
519 return _offsets[0][dy][dx];
520 break;
521
522 case MIPMAP_LEVELS:
523
524 return _offsets[lx][dy][dx];
525 break;
526
527 case RIPMAP_LEVELS:
528
529 return _offsets[lx + ly * _numXLevels][dy][dx];
530 break;
531
532 default:
533
534 throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
535 }
536 }
537
538
539 const Int64 &
operator ()(int dx,int dy,int l) const540 TileOffsets::operator () (int dx, int dy, int l) const
541 {
542 return operator () (dx, dy, l, l);
543 }
544
545 const std::vector<std::vector<std::vector <Int64> > >&
getOffsets() const546 TileOffsets::getOffsets() const
547 {
548 return _offsets;
549 }
550
551
552 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
553