Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfTiledInputFile.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
//  class TiledInputFile
38
//
39
//-----------------------------------------------------------------------------
40
41
#include "ImfTiledInputFile.h"
42
#include "ImfTileDescriptionAttribute.h"
43
#include "ImfChannelList.h"
44
#include "ImfMisc.h"
45
#include "ImfTiledMisc.h"
46
#include "ImfStdIO.h"
47
#include "ImfCompressor.h"
48
#include "ImfXdr.h"
49
#include "ImfConvert.h"
50
#include "ImfVersion.h"
51
#include "ImfTileOffsets.h"
52
#include "ImfThreading.h"
53
#include "ImfPartType.h"
54
#include "ImfMultiPartInputFile.h"
55
#include "ImfInputStreamMutex.h"
56
#include "IlmThreadPool.h"
57
#include "IlmThreadSemaphore.h"
58
#include "IlmThreadMutex.h"
59
#include "ImathVec.h"
60
#include "Iex.h"
61
#include <string>
62
#include <vector>
63
#include <algorithm>
64
#include <assert.h>
65
#include "ImfInputPartData.h"
66
#include "ImfNamespace.h"
67
68
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
69
70
using IMATH_NAMESPACE::Box2i;
71
using IMATH_NAMESPACE::V2i;
72
using std::string;
73
using std::vector;
74
using std::min;
75
using std::max;
76
using ILMTHREAD_NAMESPACE::Mutex;
77
using ILMTHREAD_NAMESPACE::Lock;
78
using ILMTHREAD_NAMESPACE::Semaphore;
79
using ILMTHREAD_NAMESPACE::Task;
80
using ILMTHREAD_NAMESPACE::TaskGroup;
81
using ILMTHREAD_NAMESPACE::ThreadPool;
82
83
namespace {
84
85
struct TInSliceInfo
86
{
87
    PixelType   typeInFrameBuffer;
88
    PixelType   typeInFile;
89
    char *      base;
90
    size_t      xStride;
91
    size_t      yStride;
92
    bool        fill;
93
    bool        skip;
94
    double      fillValue;
95
    int         xTileCoords;
96
    int         yTileCoords;
97
98
    TInSliceInfo (PixelType typeInFrameBuffer = HALF,
99
                  PixelType typeInFile = HALF,
100
                  char *base = 0,
101
                  size_t xStride = 0,
102
                  size_t yStride = 0,
103
                  bool fill = false,
104
                  bool skip = false,
105
                  double fillValue = 0.0,
106
                  int xTileCoords = 0,
107
                  int yTileCoords = 0);
108
};
109
110
111
TInSliceInfo::TInSliceInfo (PixelType tifb,
112
                            PixelType tifl,
113
                            char *b,
114
                            size_t xs, size_t ys,
115
                            bool f, bool s,
116
                            double fv,
117
                            int xtc,
118
                            int ytc)
119
:
120
    typeInFrameBuffer (tifb),
121
    typeInFile (tifl),
122
    base (b),
123
    xStride (xs),
124
    yStride (ys),
125
    fill (f),
126
    skip (s),
127
    fillValue (fv),
128
    xTileCoords (xtc),
129
    yTileCoords (ytc)
130
0
{
131
    // empty
132
0
}
133
134
135
struct TileBuffer
136
{
137
    const char *  uncompressedData;
138
    char *    buffer;
139
    int     dataSize;
140
    Compressor *  compressor;
141
    Compressor::Format  format;
142
    int     dx;
143
    int     dy;
144
    int     lx;
145
    int     ly;
146
    bool    hasException;
147
    string    exception;
148
149
     TileBuffer (Compressor * const comp);
150
    ~TileBuffer ();
151
152
0
    inline void   wait () {_sem.wait();}
153
0
    inline void   post () {_sem.post();}
154
155
 protected:
156
157
    Semaphore _sem;
158
};
159
160
161
TileBuffer::TileBuffer (Compressor *comp):
162
    uncompressedData (0),
163
    buffer (0),
164
    dataSize (0),
165
    compressor (comp),
166
    format (defaultFormat (compressor)),
167
    dx (-1),
168
    dy (-1),
169
    lx (-1),
170
    ly (-1),
171
    hasException (false),
172
    exception (),
173
    _sem (1)
174
0
{
175
    // empty
176
0
}
177
178
179
TileBuffer::~TileBuffer ()
180
0
{
181
0
    delete compressor;
182
0
}
183
184
} // namespace
185
186
187
class MultiPartInputFile;
188
189
190
//
191
// struct TiledInputFile::Data stores things that will be
192
// needed between calls to readTile()
193
//
194
195
struct TiledInputFile::Data: public Mutex
196
{
197
    Header      header;               // the image header
198
    TileDescription tileDesc;               // describes the tile layout
199
    int       version;                // file's version
200
    FrameBuffer     frameBuffer;              // framebuffer to write into
201
    LineOrder     lineOrder;                // the file's lineorder
202
    int       minX;               // data window's min x coord
203
    int       maxX;               // data window's max x coord
204
    int       minY;               // data window's min y coord
205
    int       maxY;               // data window's max x coord
206
207
    int       numXLevels;               // number of x levels
208
    int       numYLevels;               // number of y levels
209
    int *     numXTiles;                // number of x tiles at a level
210
    int *     numYTiles;                // number of y tiles at a level
211
212
    TileOffsets     tileOffsets;              // stores offsets in file for
213
    // each tile
214
215
    bool      fileIsComplete;             // True if no tiles are missing
216
                                                    // in the file
217
218
    vector<TInSliceInfo> slices;              // info about channels in file
219
220
    size_t      bytesPerPixel;                  // size of an uncompressed pixel
221
222
    size_t      maxBytesPerTileLine;            // combined size of a line
223
                                                    // over all channels
224
225
    int             partNumber;                     // part number
226
227
    bool            multiPartBackwardSupport;       // if we are reading a multipart file
228
                                                    // using OpenEXR 1.7 API
229
230
    int             numThreads;                     // number of threads
231
232
    MultiPartInputFile* multiPartFile;              // the MultiPartInputFile used to
233
                                                    // support backward compatibility
234
    
235
    vector<TileBuffer*> tileBuffers;                // each holds a single tile
236
    size_t          tileBufferSize;             // size of the tile buffers
237
238
    bool            memoryMapped;                   // if the stream is memory mapped
239
240
    InputStreamMutex * _streamData;
241
    bool                _deleteStream;
242
243
     Data (int numThreads);
244
    ~Data ();
245
246
    inline TileBuffer * getTileBuffer (int number);
247
              // hash function from tile indices
248
              // into our vector of tile buffers
249
};
250
251
252
TiledInputFile::Data::Data (int numThreads):
253
    numXTiles (0),
254
    numYTiles (0),
255
    partNumber (-1),
256
    multiPartBackwardSupport(false),
257
    numThreads(numThreads),
258
    memoryMapped(false),
259
    _streamData(NULL),
260
    _deleteStream(false)
261
0
{
262
    //
263
    // We need at least one tileBuffer, but if threading is used,
264
    // to keep n threads busy we need 2*n tileBuffers
265
    //
266
267
0
    tileBuffers.resize (max (1, 2 * numThreads));
268
0
}
269
270
271
TiledInputFile::Data::~Data ()
272
0
{
273
0
    delete [] numXTiles;
274
0
    delete [] numYTiles;
275
276
0
    for (size_t i = 0; i < tileBuffers.size(); i++)
277
0
        delete tileBuffers[i];
278
279
0
    if (multiPartBackwardSupport)
280
0
        delete multiPartFile;
281
0
}
282
283
284
TileBuffer*
285
TiledInputFile::Data::getTileBuffer (int number)
286
0
{
287
0
    return tileBuffers[number % tileBuffers.size()];
288
0
}
289
290
291
namespace {
292
293
void
294
readTileData (InputStreamMutex *streamData,
295
              TiledInputFile::Data *ifd,
296
        int dx, int dy,
297
        int lx, int ly,
298
              char *&buffer,
299
              int &dataSize)
300
0
{
301
    //
302
    // Read a single tile block from the file and into the array pointed
303
    // to by buffer.  If the file is memory-mapped, then we change where
304
    // buffer points instead of writing into the array (hence buffer needs
305
    // to be a reference to a char *).
306
    //
307
308
    //
309
    // Look up the location for this tile in the Index and
310
    // seek to that position if necessary
311
    //
312
    
313
0
    Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
314
315
0
    if (tileOffset == 0)
316
0
    {
317
0
        THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
318
0
            lx << ", " << ly << ") is missing.");
319
0
    }
320
321
322
    //
323
    // In a multi-part file, the next chunk does not need to
324
    // belong to the same part, so we have to compare the
325
    // offset here.
326
    //
327
328
0
    if (!isMultiPart(ifd->version))
329
0
    {
330
0
        if (streamData->currentPosition != tileOffset)
331
0
            streamData->is->seekg (tileOffset);
332
0
    }
333
0
    else
334
0
    {
335
        //
336
        // In a multi-part file, the file pointer may be moved by other
337
        // parts, so we have to ask tellg() where we are.
338
        //
339
0
        if (streamData->is->tellg() != tileOffset)
340
0
            streamData->is->seekg (tileOffset);
341
0
    }
342
343
    //
344
    // Read the first few bytes of the tile (the header).
345
    // Verify that the tile coordinates and the level number
346
    // are correct.
347
    //
348
    
349
0
    int tileXCoord, tileYCoord, levelX, levelY;
350
351
0
    if (isMultiPart(ifd->version))
352
0
    {
353
0
        int partNumber;
354
0
        Xdr::read <StreamIO> (*streamData->is, partNumber);
355
0
        if (partNumber != ifd->partNumber)
356
0
        {
357
0
            THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
358
0
                   << ", should be " << ifd->partNumber << ".");
359
0
        }
360
0
    }
361
362
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileXCoord);
363
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileYCoord);
364
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelX);
365
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelY);
366
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
367
368
0
    if (tileXCoord != dx)
369
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
370
371
0
    if (tileYCoord != dy)
372
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
373
374
0
    if (levelX != lx)
375
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
376
377
0
    if (levelY != ly)
378
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
379
380
0
    if (dataSize > (int) ifd->tileBufferSize)
381
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
382
383
    //
384
    // Read the pixel data.
385
    //
386
387
0
    if (streamData->is->isMemoryMapped ())
388
0
        buffer = streamData->is->readMemoryMapped (dataSize);
389
0
    else
390
0
        streamData->is->read (buffer, dataSize);
391
392
    //
393
    // Keep track of which tile is the next one in
394
    // the file, so that we can avoid redundant seekg()
395
    // operations (seekg() can be fairly expensive).
396
    //
397
    
398
0
    streamData->currentPosition = tileOffset + 5 * Xdr::size<int>() + dataSize;
399
0
}
400
401
402
void
403
readNextTileData (InputStreamMutex *streamData,
404
                  TiledInputFile::Data *ifd,
405
      int &dx, int &dy,
406
      int &lx, int &ly,
407
                  char * & buffer,
408
      int &dataSize)
409
0
{
410
    //
411
    // Read the next tile block from the file
412
    //
413
414
0
    if(isMultiPart(ifd->version))
415
0
    {
416
0
        int part;
417
0
        Xdr::read <StreamIO> (*streamData->is, part);
418
0
        if(part!=ifd->partNumber)
419
0
        {
420
0
           throw IEX_NAMESPACE::InputExc("Unexpected part number in readNextTileData");
421
0
        }
422
0
    }
423
424
    //
425
    // Read the first few bytes of the tile (the header).
426
    //
427
428
0
    Xdr::read <StreamIO> (*streamData->is, dx);
429
0
    Xdr::read <StreamIO> (*streamData->is, dy);
430
0
    Xdr::read <StreamIO> (*streamData->is, lx);
431
0
    Xdr::read <StreamIO> (*streamData->is, ly);
432
0
    Xdr::read <StreamIO> (*streamData->is, dataSize);
433
434
0
    if (dataSize > (int) ifd->tileBufferSize)
435
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
436
    
437
    //
438
    // Read the pixel data.
439
    //
440
441
0
    streamData->is->read (buffer, dataSize);
442
    
443
    //
444
    // Keep track of which tile is the next one in
445
    // the file, so that we can avoid redundant seekg()
446
    // operations (seekg() can be fairly expensive).
447
    //
448
449
0
    streamData->currentPosition += 5 * Xdr::size<int>() + dataSize;
450
0
}
451
452
453
//
454
// A TileBufferTask encapsulates the task of uncompressing
455
// a single tile and copying it into the frame buffer.
456
//
457
458
class TileBufferTask : public Task
459
{
460
  public:
461
462
    TileBufferTask (TaskGroup *group,
463
                    TiledInputFile::Data *ifd,
464
        TileBuffer *tileBuffer);
465
                    
466
    virtual ~TileBufferTask ();
467
468
    virtual void    execute ();
469
    
470
  private:
471
472
    TiledInputFile::Data *  _ifd;
473
    TileBuffer *    _tileBuffer;
474
};
475
476
477
TileBufferTask::TileBufferTask
478
    (TaskGroup *group,
479
     TiledInputFile::Data *ifd,
480
     TileBuffer *tileBuffer)
481
:
482
    Task (group),
483
    _ifd (ifd),
484
    _tileBuffer (tileBuffer)
485
0
{
486
    // empty
487
0
}
488
489
490
TileBufferTask::~TileBufferTask ()
491
0
{
492
    //
493
    // Signal that the tile buffer is now free
494
    //
495
496
0
    _tileBuffer->post ();
497
0
}
498
499
500
void
501
TileBufferTask::execute ()
502
0
{
503
0
    try
504
0
    {
505
        //
506
        // Calculate information about the tile
507
        //
508
    
509
0
        Box2i tileRange =  OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
510
0
                _ifd->tileDesc,
511
0
                _ifd->minX, _ifd->maxX,
512
0
                _ifd->minY, _ifd->maxY,
513
0
                _tileBuffer->dx,
514
0
                _tileBuffer->dy,
515
0
                _tileBuffer->lx,
516
0
                _tileBuffer->ly);
517
518
0
        int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
519
    
520
0
        int numPixelsInTile = numPixelsPerScanLine *
521
0
                            (tileRange.max.y - tileRange.min.y + 1);
522
    
523
0
        int sizeOfTile = _ifd->bytesPerPixel * numPixelsInTile;
524
    
525
    
526
        //
527
        // Uncompress the data, if necessary
528
        //
529
    
530
0
        if (_tileBuffer->compressor && _tileBuffer->dataSize < sizeOfTile)
531
0
        {
532
0
            _tileBuffer->format = _tileBuffer->compressor->format();
533
534
0
            _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
535
0
    (_tileBuffer->buffer, _tileBuffer->dataSize,
536
0
     tileRange, _tileBuffer->uncompressedData);
537
0
        }
538
0
        else
539
0
        {
540
            //
541
            // If the line is uncompressed, it's in XDR format,
542
            // regardless of the compressor's output format.
543
            //
544
    
545
0
            _tileBuffer->format = Compressor::XDR;
546
0
            _tileBuffer->uncompressedData = _tileBuffer->buffer;
547
0
        }
548
    
549
        //
550
        // Convert the tile of pixel data back from the machine-independent
551
  // representation, and store the result in the frame buffer.
552
        //
553
    
554
0
        const char *readPtr = _tileBuffer->uncompressedData;
555
                                                        // points to where we
556
                                                        // read from in the
557
                                                        // tile block
558
        
559
        //
560
        // Iterate over the scan lines in the tile.
561
        //
562
    
563
0
        for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
564
0
        {
565
            //
566
            // Iterate over all image channels.
567
            //
568
            
569
0
            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
570
0
            {
571
0
                const TInSliceInfo &slice = _ifd->slices[i];
572
    
573
                //
574
                // These offsets are used to facilitate both
575
                // absolute and tile-relative pixel coordinates.
576
                //
577
            
578
0
                int xOffset = slice.xTileCoords * tileRange.min.x;
579
0
                int yOffset = slice.yTileCoords * tileRange.min.y;
580
    
581
                //
582
                // Fill the frame buffer with pixel data.
583
                //
584
    
585
0
                if (slice.skip)
586
0
                {
587
                    //
588
                    // The file contains data for this channel, but
589
                    // the frame buffer contains no slice for this channel.
590
                    //
591
    
592
0
                    skipChannel (readPtr, slice.typeInFile,
593
0
                                 numPixelsPerScanLine);
594
0
                }
595
0
                else
596
0
                {
597
                    //
598
                    // The frame buffer contains a slice for this channel.
599
                    //
600
    
601
0
                    char *writePtr = slice.base +
602
0
                                     (y - yOffset) * slice.yStride +
603
0
                                     (tileRange.min.x - xOffset) *
604
0
                                     slice.xStride;
605
606
0
                    char *endPtr = writePtr +
607
0
                                   (numPixelsPerScanLine - 1) * slice.xStride;
608
                                    
609
0
                    copyIntoFrameBuffer (readPtr, writePtr, endPtr,
610
0
                                         slice.xStride,
611
0
                                         slice.fill, slice.fillValue,
612
0
                                         _tileBuffer->format,
613
0
                                         slice.typeInFrameBuffer,
614
0
                                         slice.typeInFile);
615
0
                }
616
0
            }
617
0
        }
618
0
    }
619
0
    catch (std::exception &e)
620
0
    {
621
0
        if (!_tileBuffer->hasException)
622
0
        {
623
0
            _tileBuffer->exception = e.what ();
624
0
            _tileBuffer->hasException = true;
625
0
        }
626
0
    }
627
0
    catch (...)
628
0
    {
629
0
        if (!_tileBuffer->hasException)
630
0
        {
631
0
            _tileBuffer->exception = "unrecognized exception";
632
0
            _tileBuffer->hasException = true;
633
0
        }
634
0
    }
635
0
}
636
637
638
TileBufferTask *
639
newTileBufferTask
640
    (TaskGroup *group,
641
     InputStreamMutex *streamData,
642
     TiledInputFile::Data *ifd,
643
     int number,
644
     int dx, int dy,
645
     int lx, int ly)
646
0
{
647
    //
648
    // Wait for a tile buffer to become available,
649
    // fill the buffer with raw data from the file,
650
    // and create a new TileBufferTask whose execute()
651
    // method will uncompress the tile and copy the
652
    // tile's pixels into the frame buffer.
653
    //
654
655
0
    TileBuffer *tileBuffer = ifd->getTileBuffer (number);
656
657
0
    try
658
0
    {
659
0
  tileBuffer->wait();
660
  
661
0
  tileBuffer->dx = dx;
662
0
  tileBuffer->dy = dy;
663
0
  tileBuffer->lx = lx;
664
0
  tileBuffer->ly = ly;
665
666
0
  tileBuffer->uncompressedData = 0;
667
668
0
  readTileData (streamData, ifd, dx, dy, lx, ly,
669
0
          tileBuffer->buffer,
670
0
          tileBuffer->dataSize);
671
0
    }
672
0
    catch (...)
673
0
    {
674
  //
675
  // Reading from the file caused an exception.
676
  // Signal that the tile buffer is free, and
677
  // re-throw the exception.
678
  //
679
680
0
  tileBuffer->post();
681
0
  throw;
682
0
    }
683
684
0
    return new TileBufferTask (group, ifd, tileBuffer);
685
0
}
686
687
688
} // namespace
689
690
691
TiledInputFile::TiledInputFile (const char fileName[], int numThreads):
692
    _data (new Data (numThreads))
693
0
{
694
0
    _data->_streamData=NULL;
695
0
    _data->_deleteStream=true;
696
    
697
    //
698
    // This constructor is called when a user
699
    // explicitly wants to read a tiled file.
700
    //
701
702
703
0
    IStream* is = 0;
704
0
    try
705
0
    {
706
0
        is = new StdIFStream (fileName);
707
0
  readMagicNumberAndVersionField(*is, _data->version);
708
709
  //
710
        // Backward compatibility to read multpart file.
711
        //
712
0
  if (isMultiPart(_data->version))
713
0
  {
714
0
      compatibilityInitialize(*is);
715
0
      return;
716
0
  }
717
718
0
  _data->_streamData = new InputStreamMutex();
719
0
  _data->_streamData->is = is;
720
0
  _data->header.readFrom (*_data->_streamData->is, _data->version);
721
0
  initialize();
722
        //read tile offsets - we are not multipart or deep
723
0
        _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
724
0
  _data->_streamData->currentPosition = _data->_streamData->is->tellg();
725
0
    }
726
0
    catch (IEX_NAMESPACE::BaseExc &e)
727
0
    {
728
0
        if (_data->_streamData != 0)
729
0
        {
730
0
            if (_data->_streamData->is != 0)
731
0
            {
732
0
                delete _data->_streamData->is;
733
0
                _data->_streamData->is = is = 0;
734
0
            }
735
736
0
            delete _data->_streamData;
737
0
        }
738
739
0
        if (is != 0)
740
0
            delete is;
741
742
0
  REPLACE_EXC (e, "Cannot open image file "
743
0
      "\"" << fileName << "\". " << e);
744
0
  throw;
745
0
    }
746
0
    catch (...)
747
0
    {
748
0
        if ( _data->_streamData != 0)
749
0
        {
750
0
            if ( _data->_streamData->is != 0)
751
0
            {
752
0
                delete _data->_streamData->is;
753
0
                _data->_streamData->is = is = 0;
754
0
            }
755
756
0
            delete _data->_streamData;
757
0
        }
758
759
0
        if (is != 0)
760
0
            delete is;
761
0
        throw;
762
0
    }
763
0
}
764
765
766
TiledInputFile::TiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
767
    _data (new Data (numThreads))
768
0
{
769
0
    _data->_deleteStream=false;
770
    //
771
    // This constructor is called when a user
772
    // explicitly wants to read a tiled file.
773
    //
774
775
0
    bool streamDataCreated = false;
776
777
0
    try
778
0
    {
779
0
  readMagicNumberAndVersionField(is, _data->version);
780
781
  //
782
  // Backward compatibility to read multpart file.
783
  //
784
0
  if (isMultiPart(_data->version))
785
0
        {
786
0
      compatibilityInitialize(is);
787
0
            return;
788
0
        }
789
790
0
  streamDataCreated = true;
791
0
  _data->_streamData = new InputStreamMutex();
792
0
  _data->_streamData->is = &is;
793
0
  _data->header.readFrom (*_data->_streamData->is, _data->version);
794
0
  initialize();
795
        // file is guaranteed to be single part, regular image
796
0
        _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
797
0
  _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
798
0
  _data->_streamData->currentPosition = _data->_streamData->is->tellg();
799
0
    }
800
0
    catch (IEX_NAMESPACE::BaseExc &e)
801
0
    {
802
0
        if (streamDataCreated) delete _data->_streamData;
803
0
  delete _data;
804
805
0
  REPLACE_EXC (e, "Cannot open image file "
806
0
      "\"" << is.fileName() << "\". " << e);
807
0
  throw;
808
0
    }
809
0
    catch (...)
810
0
    {
811
0
        if (streamDataCreated) delete _data->_streamData;
812
0
  delete _data;
813
0
        throw;
814
0
    }
815
0
}
816
817
818
TiledInputFile::TiledInputFile (const Header &header,
819
                                OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
820
                                int version,
821
                                int numThreads) :
822
    _data (new Data (numThreads))
823
0
{
824
0
    _data->_deleteStream=false;
825
0
    _data->_streamData = new InputStreamMutex();
826
    //
827
    // This constructor called by class Imf::InputFile
828
    // when a user wants to just read an image file, and
829
    // doesn't care or know if the file is tiled.
830
    // No need to have backward compatibility here, because
831
    // we have somehow got the header.
832
    //
833
834
0
    _data->_streamData->is = is;
835
0
    _data->header = header;
836
0
    _data->version = version;
837
0
    initialize();
838
0
    _data->tileOffsets.readFrom (*(_data->_streamData->is),_data->fileIsComplete,false,false);
839
0
    _data->memoryMapped = is->isMemoryMapped();
840
0
    _data->_streamData->currentPosition = _data->_streamData->is->tellg();
841
0
}
842
843
844
TiledInputFile::TiledInputFile (InputPartData* part) 
845
0
{
846
0
    _data = new Data (part->numThreads);
847
0
    _data->_deleteStream=false;
848
0
    multiPartInitialize(part);
849
0
}
850
851
852
void
853
TiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
854
0
{
855
0
    is.seekg(0);
856
    //
857
    // Construct a MultiPartInputFile, initialize TiledInputFile
858
    // with the part 0 data.
859
    // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
860
    //
861
0
    _data->multiPartBackwardSupport = true;
862
0
    _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
863
0
    InputPartData* part = _data->multiPartFile->getPart(0);
864
865
0
    multiPartInitialize(part);
866
0
}
867
868
869
void
870
TiledInputFile::multiPartInitialize(InputPartData* part)
871
0
{
872
0
    if (part->header.type() != TILEDIMAGE)
873
0
        throw IEX_NAMESPACE::ArgExc("Can't build a TiledInputFile from a type-mismatched part.");
874
875
0
    _data->_streamData = part->mutex;
876
0
    _data->header = part->header;
877
0
    _data->version = part->version;
878
0
    _data->partNumber = part->partNumber;
879
0
    _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
880
0
    initialize();
881
0
    _data->tileOffsets.readFrom(part->chunkOffsets,_data->fileIsComplete);
882
0
    _data->_streamData->currentPosition = _data->_streamData->is->tellg();
883
0
}
884
885
886
void
887
TiledInputFile::initialize ()
888
0
{
889
    // fix bad types in header (arises when a tool built against an older version of
890
    // OpenEXR converts a scanline image to tiled)
891
    // only applies when file is a single part, regular image, tiled file
892
    //
893
0
    if(!isMultiPart(_data->version) &&
894
0
       !isNonImage(_data->version) && 
895
0
       isTiled(_data->version) && 
896
0
       _data->header.hasType() )
897
0
    {
898
0
        _data->header.setType(TILEDIMAGE);
899
0
    }
900
    
901
0
    if (_data->partNumber == -1)
902
0
    {
903
0
        if (!isTiled (_data->version))
904
0
            throw IEX_NAMESPACE::ArgExc ("Expected a tiled file but the file is not tiled.");
905
        
906
0
    }
907
0
    else
908
0
    {
909
0
        if(_data->header.hasType() && _data->header.type()!=TILEDIMAGE)
910
0
        {
911
0
            throw IEX_NAMESPACE::ArgExc ("TiledInputFile used for non-tiledimage part.");
912
0
        }
913
0
    }
914
    
915
0
    _data->header.sanityCheck (true);
916
917
0
    _data->tileDesc = _data->header.tileDescription();
918
0
    _data->lineOrder = _data->header.lineOrder();
919
920
    //
921
    // Save the dataWindow information
922
    //
923
    
924
0
    const Box2i &dataWindow = _data->header.dataWindow();
925
0
    _data->minX = dataWindow.min.x;
926
0
    _data->maxX = dataWindow.max.x;
927
0
    _data->minY = dataWindow.min.y;
928
0
    _data->maxY = dataWindow.max.y;
929
930
    //
931
    // Precompute level and tile information to speed up utility functions
932
    //
933
934
0
    precalculateTileInfo (_data->tileDesc,
935
0
        _data->minX, _data->maxX,
936
0
        _data->minY, _data->maxY,
937
0
        _data->numXTiles, _data->numYTiles,
938
0
        _data->numXLevels, _data->numYLevels);    
939
940
0
    _data->bytesPerPixel = calculateBytesPerPixel (_data->header);
941
942
0
    _data->maxBytesPerTileLine = _data->bytesPerPixel * _data->tileDesc.xSize;
943
944
0
    _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
945
946
    //
947
    // Create all the TileBuffers and allocate their internal buffers
948
    //
949
950
0
    for (size_t i = 0; i < _data->tileBuffers.size(); i++)
951
0
    {
952
0
        _data->tileBuffers[i] = new TileBuffer (newTileCompressor
953
0
              (_data->header.compression(),
954
0
               _data->maxBytesPerTileLine,
955
0
               _data->tileDesc.ySize,
956
0
               _data->header));
957
958
0
        if (!_data->_streamData->is->isMemoryMapped ())
959
0
            _data->tileBuffers[i]->buffer = new char [_data->tileBufferSize];
960
0
    }
961
962
0
    _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
963
0
              _data->numXLevels,
964
0
              _data->numYLevels,
965
0
              _data->numXTiles,
966
0
              _data->numYTiles);
967
0
}
968
969
970
TiledInputFile::~TiledInputFile ()
971
0
{
972
0
    if (!_data->memoryMapped)
973
0
        for (size_t i = 0; i < _data->tileBuffers.size(); i++)
974
0
            delete [] _data->tileBuffers[i]->buffer;
975
976
0
    if (_data->_deleteStream)
977
0
        delete _data->_streamData->is;
978
979
0
    if (_data->partNumber == -1)
980
0
        delete _data->_streamData;
981
982
0
    delete _data;
983
0
}
984
985
986
const char *
987
TiledInputFile::fileName () const
988
0
{
989
0
    return _data->_streamData->is->fileName();
990
0
}
991
992
993
const Header &
994
TiledInputFile::header () const
995
0
{
996
0
    return _data->header;
997
0
}
998
999
1000
int
1001
TiledInputFile::version () const
1002
0
{
1003
0
    return _data->version;
1004
0
}
1005
1006
1007
void  
1008
TiledInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1009
0
{
1010
0
    Lock lock (*_data->_streamData);
1011
1012
    //
1013
    // Set the frame buffer
1014
    //
1015
1016
    //
1017
    // Check if the new frame buffer descriptor is
1018
    // compatible with the image file header.
1019
    //
1020
1021
0
    const ChannelList &channels = _data->header.channels();
1022
1023
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1024
0
         j != frameBuffer.end();
1025
0
         ++j)
1026
0
    {
1027
0
        ChannelList::ConstIterator i = channels.find (j.name());
1028
1029
0
        if (i == channels.end())
1030
0
            continue;
1031
1032
0
        if (i.channel().xSampling != j.slice().xSampling ||
1033
0
            i.channel().ySampling != j.slice().ySampling)
1034
0
            THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1035
0
        "of \"" << i.name() << "\" channel "
1036
0
        "of input file \"" << fileName() << "\" are "
1037
0
        "not compatible with the frame buffer's "
1038
0
        "subsampling factors.");
1039
0
    }
1040
1041
    //
1042
    // Initialize the slice table for readPixels().
1043
    //
1044
1045
0
    vector<TInSliceInfo> slices;
1046
0
    ChannelList::ConstIterator i = channels.begin();
1047
1048
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1049
0
         j != frameBuffer.end();
1050
0
         ++j)
1051
0
    {
1052
0
        while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1053
0
        {
1054
            //
1055
            // Channel i is present in the file but not
1056
            // in the frame buffer; data for channel i
1057
            // will be skipped during readPixels().
1058
            //
1059
1060
0
            slices.push_back (TInSliceInfo (i.channel().type,
1061
0
              i.channel().type,
1062
0
              0,      // base
1063
0
              0,      // xStride
1064
0
              0,      // yStride
1065
0
              false,  // fill
1066
0
              true,   // skip
1067
0
              0.0));  // fillValue
1068
0
            ++i;
1069
0
        }
1070
1071
0
        bool fill = false;
1072
1073
0
        if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1074
0
        {
1075
            //
1076
            // Channel i is present in the frame buffer, but not in the file.
1077
            // In the frame buffer, slice j will be filled with a default value.
1078
            //
1079
1080
0
            fill = true;
1081
0
        }
1082
1083
0
        slices.push_back (TInSliceInfo (j.slice().type,
1084
0
                                        fill? j.slice().type: i.channel().type,
1085
0
                                        j.slice().base,
1086
0
                                        j.slice().xStride,
1087
0
                                        j.slice().yStride,
1088
0
                                        fill,
1089
0
                                        false, // skip
1090
0
                                        j.slice().fillValue,
1091
0
                                        (j.slice().xTileCoords)? 1: 0,
1092
0
                                        (j.slice().yTileCoords)? 1: 0));
1093
1094
0
        if (i != channels.end() && !fill)
1095
0
            ++i;
1096
0
    }
1097
1098
0
    while (i != channels.end())
1099
0
    {
1100
  //
1101
  // Channel i is present in the file but not
1102
  // in the frame buffer; data for channel i
1103
  // will be skipped during readPixels().
1104
  //
1105
1106
0
  slices.push_back (TInSliceInfo (i.channel().type,
1107
0
          i.channel().type,
1108
0
          0, // base
1109
0
          0, // xStride
1110
0
          0, // yStride
1111
0
          false,  // fill
1112
0
          true, // skip
1113
0
          0.0)); // fillValue
1114
0
  ++i;
1115
0
    }
1116
1117
    //
1118
    // Store the new frame buffer.
1119
    //
1120
1121
0
    _data->frameBuffer = frameBuffer;
1122
0
    _data->slices = slices;
1123
0
}
1124
1125
1126
const FrameBuffer &
1127
TiledInputFile::frameBuffer () const
1128
0
{
1129
0
    Lock lock (*_data->_streamData);
1130
0
    return _data->frameBuffer;
1131
0
}
1132
1133
1134
bool
1135
TiledInputFile::isComplete () const
1136
0
{
1137
0
    return _data->fileIsComplete;
1138
0
}
1139
1140
1141
void
1142
TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1143
0
{
1144
    //
1145
    // Read a range of tiles from the file into the framebuffer
1146
    //
1147
1148
0
    try
1149
0
    {
1150
0
        Lock lock (*_data->_streamData);
1151
1152
0
        if (_data->slices.size() == 0)
1153
0
            throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1154
0
             "as pixel data destination.");
1155
        
1156
0
        if (!isValidLevel (lx, ly))
1157
0
            THROW (IEX_NAMESPACE::ArgExc,
1158
0
                   "Level coordinate "
1159
0
                   "(" << lx << ", " << ly << ") "
1160
0
                   "is invalid.");
1161
1162
        //
1163
        // Determine the first and last tile coordinates in both dimensions.
1164
        // We always attempt to read the range of tiles in the order that
1165
        // they are stored in the file.
1166
        //
1167
                               
1168
0
        if (dx1 > dx2)
1169
0
            std::swap (dx1, dx2);
1170
        
1171
0
        if (dy1 > dy2)
1172
0
            std::swap (dy1, dy2);
1173
        
1174
0
        int dyStart = dy1;
1175
0
  int dyStop  = dy2 + 1;
1176
0
  int dY      = 1;
1177
1178
0
        if (_data->lineOrder == DECREASING_Y)
1179
0
        {
1180
0
            dyStart = dy2;
1181
0
            dyStop  = dy1 - 1;
1182
0
            dY      = -1;
1183
0
        }
1184
1185
        //
1186
        // Create a task group for all tile buffer tasks.  When the
1187
  // task group goes out of scope, the destructor waits until
1188
  // all tasks are complete.
1189
        //
1190
        
1191
0
        {
1192
0
            TaskGroup taskGroup;
1193
0
            int tileNumber = 0;
1194
    
1195
0
            for (int dy = dyStart; dy != dyStop; dy += dY)
1196
0
            {
1197
0
                for (int dx = dx1; dx <= dx2; dx++)
1198
0
                {
1199
0
                    if (!isValidTile (dx, dy, lx, ly))
1200
0
                        THROW (IEX_NAMESPACE::ArgExc,
1201
0
             "Tile (" << dx << ", " << dy << ", " <<
1202
0
             lx << "," << ly << ") is not a valid tile.");
1203
                    
1204
0
                    ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1205
0
                                                                  _data->_streamData,
1206
0
                                                                  _data,
1207
0
                                                                  tileNumber++,
1208
0
                                                                  dx, dy,
1209
0
                                                                  lx, ly));
1210
0
                }
1211
0
            }
1212
1213
      //
1214
            // finish all tasks
1215
      //
1216
0
        }
1217
1218
  //
1219
  // Exeption handling:
1220
  //
1221
  // TileBufferTask::execute() may have encountered exceptions, but
1222
  // those exceptions occurred in another thread, not in the thread
1223
  // that is executing this call to TiledInputFile::readTiles().
1224
  // TileBufferTask::execute() has caught all exceptions and stored
1225
  // the exceptions' what() strings in the tile buffers.
1226
  // Now we check if any tile buffer contains a stored exception; if
1227
  // this is the case then we re-throw the exception in this thread.
1228
  // (It is possible that multiple tile buffers contain stored
1229
  // exceptions.  We re-throw the first exception we find and
1230
  // ignore all others.)
1231
  //
1232
1233
0
  const string *exception = 0;
1234
1235
0
        for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1236
0
  {
1237
0
            TileBuffer *tileBuffer = _data->tileBuffers[i];
1238
1239
0
      if (tileBuffer->hasException && !exception)
1240
0
    exception = &tileBuffer->exception;
1241
1242
0
      tileBuffer->hasException = false;
1243
0
  }
1244
1245
0
  if (exception)
1246
0
      throw IEX_NAMESPACE::IoExc (*exception);
1247
0
    }
1248
0
    catch (IEX_NAMESPACE::BaseExc &e)
1249
0
    {
1250
0
        REPLACE_EXC (e, "Error reading pixel data from image "
1251
0
                        "file \"" << fileName() << "\". " << e);
1252
0
        throw;
1253
0
    }
1254
0
}
1255
1256
1257
void  
1258
TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1259
0
{
1260
0
    readTiles (dx1, dx2, dy1, dy2, l, l);
1261
0
}
1262
1263
1264
void  
1265
TiledInputFile::readTile (int dx, int dy, int lx, int ly)
1266
0
{
1267
0
    readTiles (dx, dx, dy, dy, lx, ly);
1268
0
}
1269
1270
1271
void  
1272
TiledInputFile::readTile (int dx, int dy, int l)
1273
0
{
1274
0
    readTile (dx, dy, l, l);
1275
0
}
1276
1277
1278
void
1279
TiledInputFile::rawTileData (int &dx, int &dy,
1280
           int &lx, int &ly,
1281
                             const char *&pixelData,
1282
           int &pixelDataSize)
1283
0
{
1284
0
    try
1285
0
    {
1286
0
        Lock lock (*_data->_streamData);
1287
1288
0
        if (!isValidTile (dx, dy, lx, ly))
1289
0
            throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1290
0
             "the image file's data window.");
1291
1292
0
        TileBuffer *tileBuffer = _data->getTileBuffer (0);
1293
1294
        //
1295
        // if file is a multipart file, we have to seek to the required tile
1296
        // since we don't know where the file pointer is
1297
        //
1298
0
        int old_dx=dx;
1299
0
        int old_dy=dy;
1300
0
        int old_lx=lx;
1301
0
        int old_ly=ly;
1302
0
        if(isMultiPart(version()))
1303
0
        {
1304
0
            _data->_streamData->is->seekg(_data->tileOffsets(dx,dy,lx,ly));
1305
0
        }
1306
0
        readNextTileData (_data->_streamData, _data, dx, dy, lx, ly,
1307
0
        tileBuffer->buffer,
1308
0
                          pixelDataSize);
1309
0
        if(isMultiPart(version()))
1310
0
        {
1311
0
            if (old_dx!=dx || old_dy !=dy || old_lx!=lx || old_ly!=ly)
1312
0
            {
1313
0
                throw IEX_NAMESPACE::ArgExc ("rawTileData read the wrong tile");
1314
0
            }
1315
0
        }
1316
0
        pixelData = tileBuffer->buffer;
1317
0
    }
1318
0
    catch (IEX_NAMESPACE::BaseExc &e)
1319
0
    {
1320
0
        REPLACE_EXC (e, "Error reading pixel data from image "
1321
0
      "file \"" << fileName() << "\". " << e);
1322
0
        throw;
1323
0
    }
1324
0
}
1325
1326
1327
unsigned int
1328
TiledInputFile::tileXSize () const
1329
0
{
1330
0
    return _data->tileDesc.xSize;
1331
0
}
1332
1333
1334
unsigned int
1335
TiledInputFile::tileYSize () const
1336
0
{
1337
0
    return _data->tileDesc.ySize;
1338
0
}
1339
1340
1341
LevelMode
1342
TiledInputFile::levelMode () const
1343
0
{
1344
0
    return _data->tileDesc.mode;
1345
0
}
1346
1347
1348
LevelRoundingMode
1349
TiledInputFile::levelRoundingMode () const
1350
0
{
1351
0
    return _data->tileDesc.roundingMode;
1352
0
}
1353
1354
1355
int
1356
TiledInputFile::numLevels () const
1357
0
{
1358
0
    if (levelMode() == RIPMAP_LEVELS)
1359
0
  THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1360
0
            "file \"" << fileName() << "\" "
1361
0
            "(numLevels() is not defined for files "
1362
0
            "with RIPMAP level mode).");
1363
1364
0
    return _data->numXLevels;
1365
0
}
1366
1367
1368
int
1369
TiledInputFile::numXLevels () const
1370
0
{
1371
0
    return _data->numXLevels;
1372
0
}
1373
1374
1375
int
1376
TiledInputFile::numYLevels () const
1377
0
{
1378
0
    return _data->numYLevels;
1379
0
}
1380
1381
1382
bool  
1383
TiledInputFile::isValidLevel (int lx, int ly) const
1384
0
{
1385
0
    if (lx < 0 || ly < 0)
1386
0
  return false;
1387
1388
0
    if (levelMode() == MIPMAP_LEVELS && lx != ly)
1389
0
  return false;
1390
1391
0
    if (lx >= numXLevels() || ly >= numYLevels())
1392
0
  return false;
1393
1394
0
    return true;
1395
0
}
1396
1397
1398
int
1399
TiledInputFile::levelWidth (int lx) const
1400
0
{
1401
0
    try
1402
0
    {
1403
0
        return levelSize (_data->minX, _data->maxX, lx,
1404
0
        _data->tileDesc.roundingMode);
1405
0
    }
1406
0
    catch (IEX_NAMESPACE::BaseExc &e)
1407
0
    {
1408
0
  REPLACE_EXC (e, "Error calling levelWidth() on image "
1409
0
      "file \"" << fileName() << "\". " << e);
1410
0
  throw;
1411
0
    }
1412
0
}
1413
1414
1415
int
1416
TiledInputFile::levelHeight (int ly) const
1417
0
{
1418
0
    try
1419
0
    {
1420
0
        return levelSize (_data->minY, _data->maxY, ly,
1421
0
                          _data->tileDesc.roundingMode);
1422
0
    }
1423
0
    catch (IEX_NAMESPACE::BaseExc &e)
1424
0
    {
1425
0
  REPLACE_EXC (e, "Error calling levelHeight() on image "
1426
0
      "file \"" << fileName() << "\". " << e);
1427
0
  throw;
1428
0
    }
1429
0
}
1430
1431
1432
int
1433
TiledInputFile::numXTiles (int lx) const
1434
0
{
1435
0
    if (lx < 0 || lx >= _data->numXLevels)
1436
0
    {
1437
0
        THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1438
0
          "file \"" << _data->_streamData->is->fileName() << "\" "
1439
0
          "(Argument is not in valid range).");
1440
1441
0
    }
1442
    
1443
0
    return _data->numXTiles[lx];
1444
0
}
1445
1446
1447
int
1448
TiledInputFile::numYTiles (int ly) const
1449
0
{
1450
0
    if (ly < 0 || ly >= _data->numYLevels)
1451
0
    {
1452
0
        THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1453
0
          "file \"" << _data->_streamData->is->fileName() << "\" "
1454
0
          "(Argument is not in valid range).");
1455
0
    }
1456
    
1457
0
    return _data->numYTiles[ly];
1458
0
}
1459
1460
1461
Box2i
1462
TiledInputFile::dataWindowForLevel (int l) const
1463
0
{
1464
0
    return dataWindowForLevel (l, l);
1465
0
}
1466
1467
1468
Box2i
1469
TiledInputFile::dataWindowForLevel (int lx, int ly) const
1470
0
{
1471
0
    try
1472
0
    {
1473
0
  return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1474
0
          _data->tileDesc,
1475
0
          _data->minX, _data->maxX,
1476
0
          _data->minY, _data->maxY,
1477
0
          lx, ly);
1478
0
    }
1479
0
    catch (IEX_NAMESPACE::BaseExc &e)
1480
0
    {
1481
0
  REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1482
0
      "file \"" << fileName() << "\". " << e);
1483
0
  throw;
1484
0
    }
1485
0
}
1486
1487
1488
Box2i
1489
TiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1490
0
{
1491
0
    return dataWindowForTile (dx, dy, l, l);
1492
0
}
1493
1494
1495
Box2i
1496
TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1497
0
{
1498
0
    try
1499
0
    {
1500
0
  if (!isValidTile (dx, dy, lx, ly))
1501
0
      throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1502
1503
0
        return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1504
0
                _data->tileDesc,
1505
0
                _data->minX, _data->maxX,
1506
0
                _data->minY, _data->maxY,
1507
0
                dx, dy, lx, ly);
1508
0
    }
1509
0
    catch (IEX_NAMESPACE::BaseExc &e)
1510
0
    {
1511
0
  REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1512
0
      "file \"" << fileName() << "\". " << e);
1513
0
  throw;
1514
0
    }
1515
0
}
1516
1517
1518
bool
1519
TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1520
0
{
1521
0
    return ((lx < _data->numXLevels && lx >= 0) &&
1522
0
            (ly < _data->numYLevels && ly >= 0) &&
1523
0
            (dx < _data->numXTiles[lx] && dx >= 0) &&
1524
0
            (dy < _data->numYTiles[ly] && dy >= 0));
1525
0
}
1526
1527
void TiledInputFile::tileOrder(int dx[], int dy[], int lx[], int ly[]) const
1528
0
{
1529
0
   return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1530
0
}
1531
1532
1533
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT