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
47namespace Imf {
48
49
50TileOffsets::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
98bool
99TileOffsets::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
111void
112TileOffsets::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
149void
150TileOffsets::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
178void
179TileOffsets::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
217Int64
218TileOffsets::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
240bool
241TileOffsets::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
252bool
253TileOffsets::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
305Int64 &
306TileOffsets::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
338Int64 &
339TileOffsets::operator () (int dx, int dy, int l)
340{
341    return operator () (dx, dy, l, l);
342}
343
344
345const Int64 &
346TileOffsets::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
378const Int64 &
379TileOffsets::operator () (int dx, int dy, int l) const
380{
381    return operator () (dx, dy, l, l);
382}
383
384
385} // namespace Imf
386