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