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 
47 namespace Imf {
48 
49 
TileOffsets(LevelMode mode,int numXLevels,int numYLevels,const int * numXTiles,const int * numYTiles)50 TileOffsets::TileOffsets (LevelMode mode,
51               int numXLevels, int numYLevels,
52               const int *numXTiles, const int *numYTiles)
53 :
54     _mode (mode),
55     _numXLevels (numXLevels),
56     _numYLevels (numYLevels)
57 {
58     switch (_mode)
59     {
60       case ONE_LEVEL:
61       case MIPMAP_LEVELS:
62 
63         _offsets.resize (_numXLevels);
64 
65         for (unsigned int l = 0; l < _offsets.size(); ++l)
66         {
67             _offsets[l].resize (numYTiles[l]);
68 
69             for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
70         {
71                 _offsets[l][dy].resize (numXTiles[l]);
72             }
73         }
74         break;
75 
76       case RIPMAP_LEVELS:
77 
78         _offsets.resize (_numXLevels * _numYLevels);
79 
80         for (unsigned int ly = 0; ly < _numYLevels; ++ly)
81         {
82             for (unsigned int lx = 0; lx < _numXLevels; ++lx)
83             {
84                 int l = ly * _numXLevels + lx;
85                 _offsets[l].resize (numYTiles[ly]);
86 
87                 for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
88                 {
89                     _offsets[l][dy].resize (numXTiles[lx]);
90                 }
91             }
92         }
93         break;
94     }
95 }
96 
97 
98 bool
anyOffsetsAreInvalid() const99 TileOffsets::anyOffsetsAreInvalid () const
100 {
101     for (unsigned int l = 0; l < _offsets.size(); ++l)
102     for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
103         for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
104         if (_offsets[l][dy][dx] <= 0)
105             return true;
106 
107     return false;
108 }
109 
110 
111 void
findTiles(IStream & is)112 TileOffsets::findTiles (IStream &is)
113 {
114     for (unsigned int l = 0; l < _offsets.size(); ++l)
115     {
116     for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
117     {
118         for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
119         {
120         Int64 tileOffset = is.tellg();
121 
122         int tileX;
123         Xdr::read <StreamIO> (is, tileX);
124 
125         int tileY;
126         Xdr::read <StreamIO> (is, tileY);
127 
128         int levelX;
129         Xdr::read <StreamIO> (is, levelX);
130 
131         int levelY;
132         Xdr::read <StreamIO> (is, levelY);
133 
134         int dataSize;
135         Xdr::read <StreamIO> (is, dataSize);
136 
137         Xdr::skip <StreamIO> (is, dataSize);
138 
139         if (!isValidTile(tileX, tileY, levelX, levelY))
140             return;
141 
142         operator () (tileX, tileY, levelX, levelY) = tileOffset;
143         }
144     }
145     }
146 }
147 
148 
149 void
reconstructFromFile(IStream & is)150 TileOffsets::reconstructFromFile (IStream &is)
151 {
152     //
153     // Try to reconstruct a missing tile offset table by sequentially
154     // scanning through the file, and recording the offsets in the file
155     // of the tiles we find.
156     //
157 
158     Int64 position = is.tellg();
159 
160     try
161     {
162     findTiles (is);
163     }
164     catch (...)
165     {
166         //
167         // Suppress all exceptions.  This function is called only to
168     // reconstruct the tile offset table for incomplete files,
169     // and exceptions are likely.
170         //
171     }
172 
173     is.clear();
174     is.seekg (position);
175 }
176 
177 
178 void
readFrom(IStream & is,bool & complete)179 TileOffsets::readFrom (IStream &is, bool &complete)
180 {
181     //
182     // Read in the tile offsets from the file's tile offset table
183     //
184 
185     for (unsigned int l = 0; l < _offsets.size(); ++l)
186     for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
187         for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
188         Xdr::read <StreamIO> (is, _offsets[l][dy][dx]);
189 
190     //
191     // Check if any tile offsets are invalid.
192     //
193     // Invalid offsets mean that the file is probably incomplete
194     // (the offset table is the last thing written to the file).
195     // Either some process is still busy writing the file, or
196     // writing the file was aborted.
197     //
198     // We should still be able to read the existing parts of the
199     // file.  In order to do this, we have to make a sequential
200     // scan over the scan tile to reconstruct the tile offset
201     // table.
202     //
203 
204     if (anyOffsetsAreInvalid())
205     {
206     complete = false;
207     reconstructFromFile (is);
208     }
209     else
210     {
211     complete = true;
212     }
213 
214 }
215 
216 
217 Int64
writeTo(OStream & os) const218 TileOffsets::writeTo (OStream &os) const
219 {
220     //
221     // Write the tile offset table to the file, and
222     // return the position of the start of the table
223     // in the file.
224     //
225 
226     Int64 pos = os.tellp();
227 
228     if (pos == -1)
229     Iex::throwErrnoExc ("Cannot determine current file position (%T).");
230 
231     for (unsigned int l = 0; l < _offsets.size(); ++l)
232     for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
233         for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
234         Xdr::write <StreamIO> (os, _offsets[l][dy][dx]);
235 
236     return pos;
237 }
238 
239 
240 bool
isEmpty() const241 TileOffsets::isEmpty () const
242 {
243     for (unsigned int l = 0; l < _offsets.size(); ++l)
244     for (unsigned int dy = 0; dy < _offsets[l].size(); ++dy)
245         for (unsigned int dx = 0; dx < _offsets[l][dy].size(); ++dx)
246         if (_offsets[l][dy][dx] != 0)
247             return false;
248     return true;
249 }
250 
251 
252 bool
isValidTile(int dx,int dy,int lx,int ly) const253 TileOffsets::isValidTile (int dx, int dy, int lx, int ly) const
254 {
255     switch (_mode)
256     {
257       case ONE_LEVEL:
258 
259         if (lx == 0 &&
260         ly == 0 &&
261         _offsets.size() > 0 &&
262             _offsets[0].size() > dy &&
263             _offsets[0][dy].size() > dx)
264     {
265             return true;
266     }
267 
268         break;
269 
270       case MIPMAP_LEVELS:
271 
272         if (lx < _numXLevels &&
273         ly < _numYLevels &&
274             _offsets.size() > lx &&
275             _offsets[lx].size() > dy &&
276             _offsets[lx][dy].size() > dx)
277     {
278             return true;
279     }
280 
281         break;
282 
283       case RIPMAP_LEVELS:
284 
285         if (lx < _numXLevels &&
286         ly < _numYLevels &&
287             _offsets.size() > lx + ly * _numXLevels &&
288             _offsets[lx + ly * _numXLevels].size() > dy &&
289             _offsets[lx + ly * _numXLevels][dy].size() > dx)
290     {
291             return true;
292     }
293 
294         break;
295 
296       default:
297 
298         return false;
299     }
300 
301     return false;
302 }
303 
304 
305 Int64 &
operator ()(int dx,int dy,int lx,int ly)306 TileOffsets::operator () (int dx, int dy, int lx, int ly)
307 {
308     //
309     // Looks up the value of the tile with tile coordinate (dx, dy)
310     // and level number (lx, ly) in the _offsets array, and returns
311     // the cooresponding offset.
312     //
313 
314     switch (_mode)
315     {
316       case ONE_LEVEL:
317 
318         return _offsets[0][dy][dx];
319         break;
320 
321       case MIPMAP_LEVELS:
322 
323         return _offsets[lx][dy][dx];
324         break;
325 
326       case RIPMAP_LEVELS:
327 
328         return _offsets[lx + ly * _numXLevels][dy][dx];
329         break;
330 
331       default:
332 
333         throw Iex::ArgExc ("Unknown LevelMode format.");
334     }
335 }
336 
337 
338 Int64 &
operator ()(int dx,int dy,int l)339 TileOffsets::operator () (int dx, int dy, int l)
340 {
341     return operator () (dx, dy, l, l);
342 }
343 
344 
345 const Int64 &
operator ()(int dx,int dy,int lx,int ly) const346 TileOffsets::operator () (int dx, int dy, int lx, int ly) const
347 {
348     //
349     // Looks up the value of the tile with tile coordinate (dx, dy)
350     // and level number (lx, ly) in the _offsets array, and returns
351     // the cooresponding offset.
352     //
353 
354     switch (_mode)
355     {
356       case ONE_LEVEL:
357 
358         return _offsets[0][dy][dx];
359         break;
360 
361       case MIPMAP_LEVELS:
362 
363         return _offsets[lx][dy][dx];
364         break;
365 
366       case RIPMAP_LEVELS:
367 
368         return _offsets[lx + ly * _numXLevels][dy][dx];
369         break;
370 
371       default:
372 
373         throw Iex::ArgExc ("Unknown LevelMode format.");
374     }
375 }
376 
377 
378 const Int64 &
operator ()(int dx,int dy,int l) const379 TileOffsets::operator () (int dx, int dy, int l) const
380 {
381     return operator () (dx, dy, l, l);
382 }
383 
384 
385 } // namespace Imf
386