Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfDeepTiledInputFile.cpp
Line
Count
Source (jump to first uncovered line)
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2011, 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 DeepTiledInputFile
38
//
39
//-----------------------------------------------------------------------------
40
41
#include <ImfDeepTiledInputFile.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 "ImathBox.h"
49
#include <ImfXdr.h>
50
#include <ImfConvert.h>
51
#include <ImfVersion.h>
52
#include <ImfTileOffsets.h>
53
#include <ImfThreading.h>
54
#include <ImfPartType.h>
55
#include <ImfMultiPartInputFile.h>
56
#include "IlmThreadPool.h"
57
#include "IlmThreadSemaphore.h"
58
#include "IlmThreadMutex.h"
59
#include "ImfInputStreamMutex.h"
60
#include "ImfInputPartData.h"
61
#include "ImathVec.h"
62
#include "Iex.h"
63
#include <string>
64
#include <vector>
65
#include <algorithm>
66
#include <assert.h>
67
#include <limits>
68
69
#include "ImfNamespace.h"
70
71
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
72
73
using IMATH_NAMESPACE::Box2i;
74
using IMATH_NAMESPACE::V2i;
75
using std::string;
76
using std::vector;
77
using std::min;
78
using std::max;
79
using ILMTHREAD_NAMESPACE::Mutex;
80
using ILMTHREAD_NAMESPACE::Lock;
81
using ILMTHREAD_NAMESPACE::Semaphore;
82
using ILMTHREAD_NAMESPACE::Task;
83
using ILMTHREAD_NAMESPACE::TaskGroup;
84
using ILMTHREAD_NAMESPACE::ThreadPool;
85
86
namespace {
87
88
struct TInSliceInfo
89
{
90
    PixelType           typeInFrameBuffer;
91
    PixelType           typeInFile;
92
    char*               pointerArrayBase;
93
    size_t              xStride;
94
    size_t              yStride;
95
    ptrdiff_t           sampleStride;
96
    bool                fill;
97
    bool                skip;
98
    double              fillValue;
99
    int                 xTileCoords;
100
    int                 yTileCoords;
101
102
    TInSliceInfo (PixelType typeInFrameBuffer = HALF,
103
                  char * base = NULL,
104
                  PixelType typeInFile = HALF,
105
                  size_t xStride = 0,
106
                  size_t yStride = 0,
107
                  ptrdiff_t sampleStride = 0,
108
                  bool fill = false,
109
                  bool skip = false,
110
                  double fillValue = 0.0,
111
                  int xTileCoords = 0,
112
                  int yTileCoords = 0);
113
};
114
115
116
TInSliceInfo::TInSliceInfo (PixelType tifb,
117
                            char * b,
118
                            PixelType tifl,
119
                            size_t xs, size_t ys,
120
                            ptrdiff_t spst,
121
                            bool f, bool s,
122
                            double fv,
123
                            int xtc,
124
                            int ytc)
125
:
126
    typeInFrameBuffer (tifb),
127
    typeInFile (tifl),
128
    pointerArrayBase (b),
129
    xStride (xs),
130
    yStride (ys),
131
    sampleStride (spst),
132
    fill (f),
133
    skip (s),
134
    fillValue (fv),
135
    xTileCoords (xtc),
136
    yTileCoords (ytc)
137
0
{
138
    // empty
139
0
}
140
141
142
struct TileBuffer
143
{
144
    Array2D<unsigned int>       sampleCount;
145
    const char *                uncompressedData;
146
    char *                      buffer;
147
    Int64                         dataSize;
148
    Int64                         uncompressedDataSize;
149
    Compressor *                compressor;
150
    Compressor::Format          format;
151
    int                         dx;
152
    int                         dy;
153
    int                         lx;
154
    int                         ly;
155
    bool                        hasException;
156
    string                      exception;
157
158
     TileBuffer ();
159
    ~TileBuffer ();
160
161
0
    inline void         wait () {_sem.wait();}
162
0
    inline void         post () {_sem.post();}
163
164
 protected:
165
166
    Semaphore _sem;
167
};
168
169
170
TileBuffer::TileBuffer ():
171
    uncompressedData (0),
172
    buffer (0),
173
    dataSize (0),
174
    compressor (0),
175
    format (defaultFormat (compressor)),
176
    dx (-1),
177
    dy (-1),
178
    lx (-1),
179
    ly (-1),
180
    hasException (false),
181
    exception (),
182
    _sem (1)
183
0
{
184
    // empty
185
0
}
186
187
188
TileBuffer::~TileBuffer ()
189
0
{
190
0
    delete compressor;
191
0
}
192
193
} // namespace
194
195
196
class MultiPartInputFile;
197
198
199
//
200
// struct TiledInputFile::Data stores things that will be
201
// needed between calls to readTile()
202
//
203
204
struct DeepTiledInputFile::Data: public Mutex
205
{
206
    Header          header;                         // the image header
207
    TileDescription tileDesc;                       // describes the tile layout
208
    int             version;                        // file's version
209
    DeepFrameBuffer frameBuffer;                    // framebuffer to write into
210
    LineOrder       lineOrder;                      // the file's lineorder
211
    int             minX;                           // data window's min x coord
212
    int             maxX;                           // data window's max x coord
213
    int             minY;                           // data window's min y coord
214
    int             maxY;                           // data window's max x coord
215
216
    int             numXLevels;                     // number of x levels
217
    int             numYLevels;                     // number of y levels
218
    int *           numXTiles;                      // number of x tiles at a level
219
    int *           numYTiles;                      // number of y tiles at a level
220
221
    TileOffsets     tileOffsets;                    // stores offsets in file for
222
    // each tile
223
224
    bool            fileIsComplete;                 // True if no tiles are missing
225
                                                    // in the file
226
227
    vector<TInSliceInfo*> slices;                    // info about channels in file
228
229
                                                    // ourselves? or does someone
230
                                                    // else do it?
231
232
    int             partNumber;                     // part number
233
234
    bool            multiPartBackwardSupport;       // if we are reading a multipart file
235
                                                    // using OpenEXR 1.7 API
236
237
    int             numThreads;                     // number of threads
238
239
    MultiPartInputFile* multiPartFile;              // the MultiPartInputFile used to
240
                                                    // support backward compatibility
241
242
    vector<TileBuffer*> tileBuffers;                // each holds a single tile
243
244
    bool            memoryMapped;                   // if the stream is memory mapped
245
246
    char*           sampleCountSliceBase;           // pointer to the start of
247
                                                    // the sample count array
248
    ptrdiff_t       sampleCountXStride;             // x stride of the sample count array
249
    ptrdiff_t       sampleCountYStride;             // y stride of the sample count array
250
251
    int             sampleCountXTileCoords;         // the value of xTileCoords from the
252
                                                    // sample count slice
253
    int             sampleCountYTileCoords;         // the value of yTileCoords from the
254
                                                    // sample count slice
255
256
    Array<char>     sampleCountTableBuffer;         // the buffer for sample count table
257
258
    Compressor*     sampleCountTableComp;           // the decompressor for sample count table
259
260
    Int64           maxSampleCountTableSize;        // the max size in bytes for a pixel
261
                                                    // sample count table
262
    int             combinedSampleSize;             // total size of all channels combined to check sampletable size
263
                                                    
264
    InputStreamMutex *  _streamData;
265
    bool                _deleteStream; // should we delete the stream
266
     Data (int numThreads);
267
    ~Data ();
268
269
    inline TileBuffer * getTileBuffer (int number);
270
                                                    // hash function from tile indices
271
                                                    // into our vector of tile buffers
272
273
    int&                getSampleCount(int x, int y);
274
                                                    // get the number of samples
275
                                                    // in each pixel
276
};
277
278
279
DeepTiledInputFile::Data::Data (int numThreads):
280
    numXTiles (0),
281
    numYTiles (0),
282
    partNumber (-1),
283
    multiPartBackwardSupport(false),
284
    numThreads(numThreads),
285
    memoryMapped(false),
286
    _streamData(NULL),
287
    _deleteStream(false)
288
0
{
289
    //
290
    // We need at least one tileBuffer, but if threading is used,
291
    // to keep n threads busy we need 2*n tileBuffers
292
    //
293
294
0
    tileBuffers.resize (max (1, 2 * numThreads));
295
0
}
296
297
298
DeepTiledInputFile::Data::~Data ()
299
0
{
300
0
    delete [] numXTiles;
301
0
    delete [] numYTiles;
302
303
0
    for (size_t i = 0; i < tileBuffers.size(); i++)
304
0
        delete tileBuffers[i];
305
306
0
    if (multiPartBackwardSupport)
307
0
        delete multiPartFile;
308
309
0
    for (size_t i = 0; i < slices.size(); i++)
310
0
        delete slices[i];
311
0
}
312
313
314
TileBuffer*
315
DeepTiledInputFile::Data::getTileBuffer (int number)
316
0
{
317
0
    return tileBuffers[number % tileBuffers.size()];
318
0
}
319
320
321
int&
322
DeepTiledInputFile::Data::getSampleCount(int x, int y)
323
0
{
324
0
    return sampleCount(sampleCountSliceBase,
325
0
                       sampleCountXStride,
326
0
                       sampleCountYStride,
327
0
                       x, y);
328
0
}
329
330
331
namespace {
332
333
void
334
readTileData (InputStreamMutex *streamData,
335
              DeepTiledInputFile::Data *ifd,
336
              int dx, int dy,
337
              int lx, int ly,
338
              char *&buffer,
339
              Int64 &dataSize,
340
              Int64 &unpackedDataSize)
341
0
{
342
    //
343
    // Read a single tile block from the file and into the array pointed
344
    // to by buffer.  If the file is memory-mapped, then we change where
345
    // buffer points instead of writing into the array (hence buffer needs
346
    // to be a reference to a char *).
347
    //
348
349
    //
350
    // Look up the location for this tile in the Index and
351
    // seek to that position if necessary
352
    //
353
354
0
    Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
355
356
0
    if (tileOffset == 0)
357
0
    {
358
0
        THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
359
0
                              lx << ", " << ly << ") is missing.");
360
0
    }
361
362
    //
363
    // In a multi-part file, the next chunk does not need to
364
    // belong to the same part, so we have to compare the
365
    // offset here.
366
    //
367
368
0
    if ( !isMultiPart(ifd->version) )
369
0
    {
370
0
        if (streamData->currentPosition != tileOffset)
371
0
            streamData->is->seekg(tileOffset);
372
0
    }
373
0
    else
374
0
    {
375
        //
376
        // In a multi-part file, the file pointer may be moved by other
377
        // parts, so we have to ask tellg() where we are.
378
        //
379
0
        if (streamData->is->tellg() != tileOffset)
380
0
            streamData->is->seekg (tileOffset);
381
0
    }
382
383
    //
384
    // Read the first few bytes of the tile (the header).
385
    // Verify that the tile coordinates and the level number
386
    // are correct.
387
    //
388
389
0
    int tileXCoord, tileYCoord, levelX, levelY;
390
391
0
    if (isMultiPart(ifd->version))
392
0
    {
393
0
        int partNumber;
394
0
        Xdr::read <StreamIO> (*streamData->is, partNumber);
395
0
        if (partNumber != ifd->partNumber)
396
0
        {
397
0
            THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
398
0
                   << ", should be " << ifd->partNumber << ".");
399
0
        }
400
0
    }
401
402
0
    Xdr::read <StreamIO> (*streamData->is, tileXCoord);
403
0
    Xdr::read <StreamIO> (*streamData->is, tileYCoord);
404
0
    Xdr::read <StreamIO> (*streamData->is, levelX);
405
0
    Xdr::read <StreamIO> (*streamData->is, levelY);
406
407
0
    Int64 tableSize;
408
0
    Xdr::read <StreamIO> (*streamData->is, tableSize);
409
410
0
    Xdr::read <StreamIO> (*streamData->is, dataSize);
411
0
    Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
412
413
414
    //
415
    // Skip the pixel sample count table because we have read this data.
416
    //
417
418
0
    Xdr::skip <StreamIO> (*streamData->is, tableSize);
419
420
421
0
    if (tileXCoord != dx)
422
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
423
424
0
    if (tileYCoord != dy)
425
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
426
427
0
    if (levelX != lx)
428
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
429
430
0
    if (levelY != ly)
431
0
        throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
432
433
    //
434
    // Read the pixel data.
435
    //
436
437
0
    if (streamData->is->isMemoryMapped ())
438
0
        buffer = streamData->is->readMemoryMapped (dataSize);
439
0
    else
440
0
    {
441
        // (TODO) check if the packed data size is too big?
442
        // (TODO) better memory management here. Don't delete buffer everytime.
443
0
        if (buffer != 0) delete[] buffer;
444
0
        buffer = new char[dataSize];
445
0
        streamData->is->read (buffer, dataSize);
446
0
    }
447
448
    //
449
    // Keep track of which tile is the next one in
450
    // the file, so that we can avoid redundant seekg()
451
    // operations (seekg() can be fairly expensive).
452
    //
453
454
0
    streamData->currentPosition = tileOffset + 4 * Xdr::size<int>() +
455
0
                                  3 * Xdr::size<Int64>()            +
456
0
                                  tableSize                         +
457
0
                                  dataSize;
458
0
}
459
460
461
void
462
readNextTileData (InputStreamMutex *streamData,
463
                  DeepTiledInputFile::Data *ifd,
464
                  int &dx, int &dy,
465
                  int &lx, int &ly,
466
                  char * & buffer,
467
                  Int64 &dataSize,
468
                  Int64 &unpackedDataSize)
469
0
{
470
0
    //
471
0
    // Read the next tile block from the file
472
0
    //
473
0
474
0
    //
475
0
    // Read the first few bytes of the tile (the header).
476
0
    //
477
0
478
0
    Xdr::read <StreamIO> (*streamData->is, dx);
479
0
    Xdr::read <StreamIO> (*streamData->is, dy);
480
0
    Xdr::read <StreamIO> (*streamData->is, lx);
481
0
    Xdr::read <StreamIO> (*streamData->is, ly);
482
0
483
0
    Int64 tableSize;
484
0
    Xdr::read <StreamIO> (*streamData->is, tableSize);
485
0
486
0
    Xdr::read <StreamIO> (*streamData->is, dataSize);
487
0
    Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
488
0
489
0
    //
490
0
    // Skip the pixel sample count table because we have read this data.
491
0
    //
492
0
493
0
    Xdr::skip <StreamIO> (*streamData->is, tableSize);
494
0
495
0
    //
496
0
    // Read the pixel data.
497
0
    //
498
0
499
0
    streamData->is->read (buffer, dataSize);
500
0
501
0
    //
502
0
    // Keep track of which tile is the next one in
503
0
    // the file, so that we can avoid redundant seekg()
504
0
    // operations (seekg() can be fairly expensive).
505
0
    //
506
0
507
0
    streamData->currentPosition += 4 * Xdr::size<int>()   +
508
0
                                   3 * Xdr::size<Int64>() +
509
0
                                   tableSize              +
510
0
                                   dataSize;
511
0
}
512
513
514
//
515
// A TileBufferTask encapsulates the task of uncompressing
516
// a single tile and copying it into the frame buffer.
517
//
518
519
class TileBufferTask : public Task
520
{
521
  public:
522
523
    TileBufferTask (TaskGroup *group,
524
                    DeepTiledInputFile::Data *ifd,
525
                    TileBuffer *tileBuffer);
526
527
    virtual ~TileBufferTask ();
528
529
    virtual void                execute ();
530
531
  private:
532
533
    DeepTiledInputFile::Data *      _ifd;
534
    TileBuffer *                _tileBuffer;
535
};
536
537
538
TileBufferTask::TileBufferTask
539
    (TaskGroup *group,
540
     DeepTiledInputFile::Data *ifd,
541
     TileBuffer *tileBuffer)
542
:
543
    Task (group),
544
    _ifd (ifd),
545
    _tileBuffer (tileBuffer)
546
0
{
547
    // empty
548
0
}
549
550
551
TileBufferTask::~TileBufferTask ()
552
0
{
553
    //
554
    // Signal that the tile buffer is now free
555
    //
556
557
0
    _tileBuffer->post ();
558
0
}
559
560
561
void
562
TileBufferTask::execute ()
563
0
{
564
0
    try
565
0
    {
566
        //
567
        // Calculate information about the tile
568
        //
569
570
0
        Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
571
0
                _ifd->tileDesc,
572
0
                _ifd->minX, _ifd->maxX,
573
0
                _ifd->minY, _ifd->maxY,
574
0
                _tileBuffer->dx,
575
0
                _tileBuffer->dy,
576
0
                _tileBuffer->lx,
577
0
                _tileBuffer->ly);
578
579
        //
580
        // Get the size of the tile.
581
        //
582
583
0
        Array<unsigned int> numPixelsPerScanLine;
584
0
        numPixelsPerScanLine.resizeErase(tileRange.max.y - tileRange.min.y + 1);
585
586
0
        int sizeOfTile = 0;
587
0
        int maxBytesPerTileLine = 0;
588
589
0
        for (int y = tileRange.min.y; y <= tileRange.max.y; y++)
590
0
        {
591
0
            numPixelsPerScanLine[y - tileRange.min.y] = 0;
592
593
0
            int bytesPerLine = 0;
594
595
0
            for (int x = tileRange.min.x; x <= tileRange.max.x; x++)
596
0
            {
597
0
                int xOffset = _ifd->sampleCountXTileCoords * tileRange.min.x;
598
0
                int yOffset = _ifd->sampleCountYTileCoords * tileRange.min.y;
599
600
0
                int count = _ifd->getSampleCount(x - xOffset, y - yOffset);
601
0
                for (unsigned int c = 0; c < _ifd->slices.size(); ++c)
602
0
                {
603
0
                    sizeOfTile += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
604
0
                    bytesPerLine += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
605
0
                }
606
0
                numPixelsPerScanLine[y - tileRange.min.y] += count;
607
0
            }
608
609
0
            if (bytesPerLine > maxBytesPerTileLine)
610
0
                maxBytesPerTileLine = bytesPerLine;
611
0
        }
612
613
        // (TODO) don't do this every time.
614
0
        if (_tileBuffer->compressor != 0)
615
0
            delete _tileBuffer->compressor;
616
0
        _tileBuffer->compressor = newTileCompressor
617
0
                                  (_ifd->header.compression(),
618
0
                                   maxBytesPerTileLine,
619
0
                                   _ifd->tileDesc.ySize,
620
0
                                   _ifd->header);
621
622
        //
623
        // Uncompress the data, if necessary
624
        //
625
626
0
        if (_tileBuffer->compressor && _tileBuffer->dataSize < Int64(sizeOfTile))
627
0
        {
628
0
            _tileBuffer->format = _tileBuffer->compressor->format();
629
630
0
            _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
631
0
                (_tileBuffer->buffer, _tileBuffer->dataSize,
632
0
                 tileRange, _tileBuffer->uncompressedData);
633
0
        }
634
0
        else
635
0
        {
636
            //
637
            // If the line is uncompressed, it's in XDR format,
638
            // regardless of the compressor's output format.
639
            //
640
641
0
            _tileBuffer->format = Compressor::XDR;
642
0
            _tileBuffer->uncompressedData = _tileBuffer->buffer;
643
0
        }
644
645
        //
646
        // Convert the tile of pixel data back from the machine-independent
647
        // representation, and store the result in the frame buffer.
648
        //
649
650
0
        const char *readPtr = _tileBuffer->uncompressedData;
651
                                                        // points to where we
652
                                                        // read from in the
653
                                                        // tile block
654
655
        //
656
        // Iterate over the scan lines in the tile.
657
        //
658
659
0
        for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
660
0
        {
661
            //
662
            // Iterate over all image channels.
663
            //
664
665
0
            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
666
0
            {
667
0
                TInSliceInfo &slice = *_ifd->slices[i];
668
669
                //
670
                // These offsets are used to facilitate both
671
                // absolute and tile-relative pixel coordinates.
672
                //
673
674
0
                int xOffsetForData = (slice.xTileCoords == 0) ? 0 : tileRange.min.x;
675
0
                int yOffsetForData = (slice.yTileCoords == 0) ? 0 : tileRange.min.y;
676
0
                int xOffsetForSampleCount =
677
0
                        (_ifd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x;
678
0
                int yOffsetForSampleCount =
679
0
                        (_ifd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y;
680
681
                //
682
                // Fill the frame buffer with pixel data.
683
                //
684
685
0
                if (slice.skip)
686
0
                {
687
                    //
688
                    // The file contains data for this channel, but
689
                    // the frame buffer contains no slice for this channel.
690
                    //
691
692
0
                    skipChannel (readPtr, slice.typeInFile,
693
0
                                 numPixelsPerScanLine[y - tileRange.min.y]);
694
0
                }
695
0
                else
696
0
                {
697
                    //
698
                    // The frame buffer contains a slice for this channel.
699
                    //
700
701
0
                    copyIntoDeepFrameBuffer (readPtr, slice.pointerArrayBase,
702
0
                                             _ifd->sampleCountSliceBase,
703
0
                                             _ifd->sampleCountXStride,
704
0
                                             _ifd->sampleCountYStride,
705
0
                                             y,
706
0
                                             tileRange.min.x,
707
0
                                             tileRange.max.x,
708
0
                                             xOffsetForSampleCount, yOffsetForSampleCount,
709
0
                                             xOffsetForData, yOffsetForData,
710
0
                                             slice.sampleStride, 
711
0
                                             slice.xStride,
712
0
                                             slice.yStride,
713
0
                                             slice.fill,
714
0
                                             slice.fillValue, _tileBuffer->format,
715
0
                                             slice.typeInFrameBuffer,
716
0
                                             slice.typeInFile);
717
0
                }
718
0
            }
719
0
        }
720
0
    }
721
0
    catch (std::exception &e)
722
0
    {
723
0
        if (!_tileBuffer->hasException)
724
0
        {
725
0
            _tileBuffer->exception = e.what ();
726
0
            _tileBuffer->hasException = true;
727
0
        }
728
0
    }
729
0
    catch (...)
730
0
    {
731
0
        if (!_tileBuffer->hasException)
732
0
        {
733
0
            _tileBuffer->exception = "unrecognized exception";
734
0
            _tileBuffer->hasException = true;
735
0
        }
736
0
    }
737
0
}
738
739
740
TileBufferTask *
741
newTileBufferTask
742
    (TaskGroup *group,
743
     DeepTiledInputFile::Data *ifd,
744
     int number,
745
     int dx, int dy,
746
     int lx, int ly)
747
0
{
748
    //
749
    // Wait for a tile buffer to become available,
750
    // fill the buffer with raw data from the file,
751
    // and create a new TileBufferTask whose execute()
752
    // method will uncompress the tile and copy the
753
    // tile's pixels into the frame buffer.
754
    //
755
756
0
    TileBuffer *tileBuffer = ifd->getTileBuffer (number);
757
758
0
    try
759
0
    {
760
0
        tileBuffer->wait();
761
762
0
        tileBuffer->dx = dx;
763
0
        tileBuffer->dy = dy;
764
0
        tileBuffer->lx = lx;
765
0
        tileBuffer->ly = ly;
766
767
0
        tileBuffer->uncompressedData = 0;
768
769
0
        readTileData (ifd->_streamData, ifd, dx, dy, lx, ly,
770
0
                      tileBuffer->buffer,
771
0
                      tileBuffer->dataSize,
772
0
                      tileBuffer->uncompressedDataSize);
773
0
    }
774
0
    catch (...)
775
0
    {
776
        //
777
        // Reading from the file caused an exception.
778
        // Signal that the tile buffer is free, and
779
        // re-throw the exception.
780
        //
781
782
0
        tileBuffer->post();
783
0
        throw;
784
0
    }
785
786
0
    return new TileBufferTask (group, ifd, tileBuffer);
787
0
}
788
789
790
} // namespace
791
792
793
DeepTiledInputFile::DeepTiledInputFile (const char fileName[], int numThreads):
794
    _data (new Data (numThreads))
795
0
{
796
0
    _data->_deleteStream=true;
797
    //
798
    // This constructor is called when a user
799
    // explicitly wants to read a tiled file.
800
    //
801
802
0
    IStream* is = 0;
803
0
    try
804
0
    {
805
0
        is = new StdIFStream (fileName);
806
0
        readMagicNumberAndVersionField(*is, _data->version);
807
808
        //
809
        // Compatibility to read multpart file.
810
        //
811
0
        if (isMultiPart(_data->version))
812
0
        {
813
0
            compatibilityInitialize(*is);
814
0
        }
815
0
        else
816
0
        {
817
0
            _data->_streamData = new InputStreamMutex();
818
0
            _data->_streamData->is = is;
819
0
            _data->header.readFrom (*_data->_streamData->is, _data->version);
820
0
            initialize();
821
0
            _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
822
0
            _data->_streamData->currentPosition = _data->_streamData->is->tellg();
823
0
        }
824
0
    }
825
0
    catch (IEX_NAMESPACE::BaseExc &e)
826
0
    {
827
0
        if (is)          delete is;
828
0
        if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
829
0
        if (_data)       delete _data;
830
831
0
        REPLACE_EXC (e, "Cannot open image file "
832
0
                        "\"" << fileName << "\". " << e);
833
0
        throw;
834
0
    }
835
0
    catch (...)
836
0
    {
837
0
        if (is)          delete is;
838
0
        if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
839
0
        if (_data)       delete _data;
840
841
0
        throw;
842
0
    }
843
0
}
844
845
846
DeepTiledInputFile::DeepTiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
847
    _data (new Data (numThreads))
848
0
{
849
0
    _data->_streamData=0;
850
0
    _data->_deleteStream=false;
851
    
852
    //
853
    // This constructor is called when a user
854
    // explicitly wants to read a tiled file.
855
    //
856
857
0
    try
858
0
    {
859
0
        readMagicNumberAndVersionField(is, _data->version);
860
861
        //
862
        // Backward compatibility to read multpart file.
863
        //
864
0
        if (isMultiPart(_data->version))
865
0
        {
866
0
            compatibilityInitialize(is);
867
0
        }
868
0
        else
869
0
        {
870
0
            _data->_streamData = new InputStreamMutex();
871
0
            _data->_streamData->is = &is;
872
0
            _data->header.readFrom (*_data->_streamData->is, _data->version);
873
0
            initialize();
874
            // file is guaranteed not to be multipart, but is deep
875
0
            _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete, false,true);
876
0
            _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
877
0
            _data->_streamData->currentPosition = _data->_streamData->is->tellg();
878
0
        }
879
0
    }
880
0
    catch (IEX_NAMESPACE::BaseExc &e)
881
0
    {
882
0
        if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
883
0
        if (_data)       delete _data;
884
885
0
        REPLACE_EXC (e, "Cannot open image file "
886
0
                        "\"" << is.fileName() << "\". " << e);
887
0
        throw;
888
0
    }
889
0
    catch (...)
890
0
    {
891
0
        if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
892
0
        if (_data)       delete _data;
893
894
0
        throw;
895
0
    }
896
0
}
897
898
899
DeepTiledInputFile::DeepTiledInputFile (const Header &header,
900
                                        OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
901
                                        int version,
902
                                        int numThreads) :
903
    _data (new Data (numThreads))
904
    
905
0
{
906
0
    _data->_streamData->is = is;
907
0
    _data->_deleteStream=false;
908
    
909
    //
910
    // This constructor called by class Imf::InputFile
911
    // when a user wants to just read an image file, and
912
    // doesn't care or know if the file is tiled.
913
    // No need to have backward compatibility here, because
914
    // we have the header.
915
    //
916
917
0
    _data->header = header;
918
0
    _data->version = version;
919
0
    initialize();
920
0
    _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
921
0
    _data->memoryMapped = is->isMemoryMapped();
922
0
    _data->_streamData->currentPosition = _data->_streamData->is->tellg();
923
0
}
924
925
926
DeepTiledInputFile::DeepTiledInputFile (InputPartData* part) :
927
    _data (new Data (part->numThreads))
928
0
{
929
0
    _data->_deleteStream=false;
930
0
    multiPartInitialize(part);
931
0
}
932
933
934
void
935
DeepTiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
936
0
{
937
0
    is.seekg(0);
938
    //
939
    // Construct a MultiPartInputFile, initialize TiledInputFile
940
    // with the part 0 data.
941
    // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
942
    //
943
0
    _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
944
0
    _data->multiPartBackwardSupport = true;
945
0
    InputPartData* part = _data->multiPartFile->getPart(0);
946
947
0
    multiPartInitialize(part);
948
0
}
949
950
951
void
952
DeepTiledInputFile::multiPartInitialize(InputPartData* part)
953
0
{
954
0
    if (isTiled(part->header.type()) == false)
955
0
        THROW (IEX_NAMESPACE::ArgExc, "Can't build a DeepTiledInputFile from a part of type " << part->header.type());
956
957
0
    _data->_streamData = part->mutex;
958
0
    _data->header = part->header;
959
0
    _data->version = part->version;
960
0
    _data->partNumber = part->partNumber;
961
0
    _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
962
0
    initialize();
963
0
    _data->tileOffsets.readFrom(part->chunkOffsets , _data->fileIsComplete);
964
0
    _data->_streamData->currentPosition = _data->_streamData->is->tellg();
965
0
}
966
967
968
void
969
DeepTiledInputFile::initialize ()
970
0
{
971
0
    if (_data->partNumber == -1)
972
0
        if (_data->header.type() != DEEPTILE)
973
0
            throw IEX_NAMESPACE::ArgExc ("Expected a deep tiled file but the file is not deep tiled.");
974
0
   if(_data->header.version()!=1)
975
0
   {
976
0
       THROW(IEX_NAMESPACE::ArgExc, "Version " << _data->header.version() << " not supported for deeptiled images in this version of the library");
977
0
   }
978
        
979
0
    _data->header.sanityCheck (true);
980
981
0
    _data->tileDesc = _data->header.tileDescription();
982
0
    _data->lineOrder = _data->header.lineOrder();
983
984
    //
985
    // Save the dataWindow information
986
    //
987
988
0
    const Box2i &dataWindow = _data->header.dataWindow();
989
0
    _data->minX = dataWindow.min.x;
990
0
    _data->maxX = dataWindow.max.x;
991
0
    _data->minY = dataWindow.min.y;
992
0
    _data->maxY = dataWindow.max.y;
993
994
    //
995
    // Precompute level and tile information to speed up utility functions
996
    //
997
998
0
    precalculateTileInfo (_data->tileDesc,
999
0
                          _data->minX, _data->maxX,
1000
0
                          _data->minY, _data->maxY,
1001
0
                          _data->numXTiles, _data->numYTiles,
1002
0
                          _data->numXLevels, _data->numYLevels);
1003
1004
    //
1005
    // Create all the TileBuffers and allocate their internal buffers
1006
    //
1007
1008
0
    _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
1009
0
                                      _data->numXLevels,
1010
0
                                      _data->numYLevels,
1011
0
                                      _data->numXTiles,
1012
0
                                      _data->numYTiles);
1013
1014
0
    for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1015
0
        _data->tileBuffers[i] = new TileBuffer ();
1016
1017
0
    _data->maxSampleCountTableSize = _data->tileDesc.ySize *
1018
0
                                     _data->tileDesc.xSize *
1019
0
                                     sizeof(int);
1020
1021
0
    _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize);
1022
1023
0
    _data->sampleCountTableComp = newCompressor(_data->header.compression(),
1024
0
                                                _data->maxSampleCountTableSize,
1025
0
                                                _data->header);
1026
                                                
1027
                                                
1028
0
    const ChannelList & c=_data->header.channels();
1029
0
    _data->combinedSampleSize=0;
1030
0
    for(ChannelList::ConstIterator i=c.begin();i!=c.end();i++)
1031
0
    {
1032
0
        switch( i.channel().type )
1033
0
        {
1034
0
            case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF  :
1035
0
                _data->combinedSampleSize+=Xdr::size<half>();
1036
0
                break;
1037
0
            case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1038
0
                _data->combinedSampleSize+=Xdr::size<float>();
1039
0
                break;
1040
0
            case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT  :
1041
0
                _data->combinedSampleSize+=Xdr::size<unsigned int>();
1042
0
                break;
1043
0
            default :
1044
0
                THROW(IEX_NAMESPACE::ArgExc, "Bad type for channel " << i.name() << " initializing deepscanline reader");
1045
0
        }
1046
0
    }
1047
                                                  
1048
0
}
1049
1050
1051
DeepTiledInputFile::~DeepTiledInputFile ()
1052
0
{
1053
0
    if (!_data->memoryMapped)
1054
0
        for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1055
0
            if (_data->tileBuffers[i]->buffer != 0)
1056
0
                delete [] _data->tileBuffers[i]->buffer;
1057
1058
0
    if (_data->_deleteStream)
1059
0
        delete _data->_streamData->is;
1060
1061
    //
1062
    // (TODO) we should have a way to tell if the stream data is owned by this file or
1063
    // by a parent multipart file.
1064
    //
1065
1066
0
    if (_data->partNumber == -1)
1067
0
        delete _data->_streamData;
1068
1069
0
    delete _data;
1070
0
}
1071
1072
1073
const char *
1074
DeepTiledInputFile::fileName () const
1075
0
{
1076
0
    return _data->_streamData->is->fileName();
1077
0
}
1078
1079
1080
const Header &
1081
DeepTiledInputFile::header () const
1082
0
{
1083
0
    return _data->header;
1084
0
}
1085
1086
1087
int
1088
DeepTiledInputFile::version () const
1089
0
{
1090
0
    return _data->version;
1091
0
}
1092
1093
1094
void
1095
DeepTiledInputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
1096
0
{
1097
0
    Lock lock (*_data->_streamData);
1098
1099
    //
1100
    // Set the frame buffer
1101
    //
1102
1103
    //
1104
    // Check if the new frame buffer descriptor is
1105
    // compatible with the image file header.
1106
    //
1107
1108
0
    const ChannelList &channels = _data->header.channels();
1109
1110
0
    for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1111
0
         j != frameBuffer.end();
1112
0
         ++j)
1113
0
    {
1114
0
        ChannelList::ConstIterator i = channels.find (j.name());
1115
1116
0
        if (i == channels.end())
1117
0
            continue;
1118
1119
0
        if (i.channel().xSampling != j.slice().xSampling ||
1120
0
            i.channel().ySampling != j.slice().ySampling)
1121
0
            THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1122
0
                                "of \"" << i.name() << "\" channel "
1123
0
                                "of input file \"" << fileName() << "\" are "
1124
0
                                "not compatible with the frame buffer's "
1125
0
                                "subsampling factors.");
1126
0
    }
1127
1128
    //
1129
    // Store the pixel sample count table.
1130
    // (TODO) Support for different sampling rates?
1131
    //
1132
1133
0
    const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
1134
0
    if (sampleCountSlice.base == 0)
1135
0
    {
1136
0
        throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
1137
0
    }
1138
0
    else
1139
0
    {
1140
0
        _data->sampleCountSliceBase = sampleCountSlice.base;
1141
0
        _data->sampleCountXStride = sampleCountSlice.xStride;
1142
0
        _data->sampleCountYStride = sampleCountSlice.yStride;
1143
0
        _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords;
1144
0
        _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords;
1145
0
    }
1146
1147
    //
1148
    // Initialize the slice table for readPixels().
1149
    //
1150
1151
0
    vector<TInSliceInfo*> slices;
1152
0
    ChannelList::ConstIterator i = channels.begin();
1153
1154
0
    for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1155
0
         j != frameBuffer.end();
1156
0
         ++j)
1157
0
    {
1158
0
        while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1159
0
        {
1160
            //
1161
            // Channel i is present in the file but not
1162
            // in the frame buffer; data for channel i
1163
            // will be skipped during readPixels().
1164
            //
1165
1166
0
            slices.push_back (new TInSliceInfo (i.channel().type,
1167
0
                                                NULL,
1168
0
                                                i.channel().type,
1169
0
                                                0,      // xStride
1170
0
                                                0,      // yStride
1171
0
                                                0,      // sampleStride
1172
0
                                                false,  // fill
1173
0
                                                true,   // skip
1174
0
                                                0.0));  // fillValue
1175
0
            ++i;
1176
0
        }
1177
1178
0
        bool fill = false;
1179
1180
0
        if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1181
0
        {
1182
            //
1183
            // Channel i is present in the frame buffer, but not in the file.
1184
            // In the frame buffer, slice j will be filled with a default value.
1185
            //
1186
1187
0
            fill = true;
1188
0
        }
1189
1190
0
        slices.push_back (new TInSliceInfo (j.slice().type,
1191
0
                                            j.slice().base,
1192
0
                                            fill? j.slice().type: i.channel().type,
1193
0
                                            j.slice().xStride,
1194
0
                                            j.slice().yStride,
1195
0
                                            j.slice().sampleStride,
1196
0
                                            fill,
1197
0
                                            false, // skip
1198
0
                                            j.slice().fillValue,
1199
0
                                            (j.slice().xTileCoords)? 1: 0,
1200
0
                                            (j.slice().yTileCoords)? 1: 0));
1201
1202
1203
0
        if (i != channels.end() && !fill)
1204
0
            ++i;
1205
0
    }
1206
1207
    // (TODO) inspect the following code. It's additional to the scanline input file.
1208
    // Is this needed?
1209
1210
0
    while (i != channels.end())
1211
0
    {
1212
        //
1213
        // Channel i is present in the file but not
1214
        // in the frame buffer; data for channel i
1215
        // will be skipped during readPixels().
1216
        //
1217
1218
0
        slices.push_back (new TInSliceInfo (i.channel().type,
1219
0
                                            NULL,
1220
0
                                            i.channel().type,
1221
0
                                            0, // xStride
1222
0
                                            0, // yStride
1223
0
                                            0, // sampleStride
1224
0
                                            false,  // fill
1225
0
                                            true, // skip
1226
0
                                            0.0)); // fillValue
1227
0
        ++i;
1228
0
    }
1229
1230
    //
1231
    // Store the new frame buffer.
1232
    //
1233
1234
0
    _data->frameBuffer = frameBuffer;
1235
1236
0
    for (size_t i = 0; i < _data->slices.size(); i++)
1237
0
        delete _data->slices[i];
1238
0
    _data->slices = slices;
1239
0
}
1240
1241
1242
const DeepFrameBuffer &
1243
DeepTiledInputFile::frameBuffer () const
1244
0
{
1245
0
    Lock lock (*_data->_streamData);
1246
0
    return _data->frameBuffer;
1247
0
}
1248
1249
1250
bool
1251
DeepTiledInputFile::isComplete () const
1252
0
{
1253
0
    return _data->fileIsComplete;
1254
0
}
1255
1256
1257
void
1258
DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1259
0
{
1260
    //
1261
    // Read a range of tiles from the file into the framebuffer
1262
    //
1263
1264
0
    try
1265
0
    {
1266
0
        Lock lock (*_data->_streamData);
1267
1268
0
        if (_data->slices.size() == 0)
1269
0
            throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1270
0
                               "as pixel data destination.");
1271
1272
0
        if (!isValidLevel (lx, ly))
1273
0
            THROW (IEX_NAMESPACE::ArgExc,
1274
0
                   "Level coordinate "
1275
0
                   "(" << lx << ", " << ly << ") "
1276
0
                   "is invalid.");
1277
1278
        //
1279
        // Determine the first and last tile coordinates in both dimensions.
1280
        // We always attempt to read the range of tiles in the order that
1281
        // they are stored in the file.
1282
        //
1283
1284
0
        if (dx1 > dx2)
1285
0
            std::swap (dx1, dx2);
1286
1287
0
        if (dy1 > dy2)
1288
0
            std::swap (dy1, dy2);
1289
1290
0
        int dyStart = dy1;
1291
0
        int dyStop  = dy2 + 1;
1292
0
        int dY      = 1;
1293
1294
0
        if (_data->lineOrder == DECREASING_Y)
1295
0
        {
1296
0
            dyStart = dy2;
1297
0
            dyStop  = dy1 - 1;
1298
0
            dY      = -1;
1299
0
        }
1300
1301
        //
1302
        // Create a task group for all tile buffer tasks.  When the
1303
        // task group goes out of scope, the destructor waits until
1304
        // all tasks are complete.
1305
        //
1306
1307
0
        {
1308
0
            TaskGroup taskGroup;
1309
0
            int tileNumber = 0;
1310
1311
0
            for (int dy = dyStart; dy != dyStop; dy += dY)
1312
0
            {
1313
0
                for (int dx = dx1; dx <= dx2; dx++)
1314
0
                {
1315
0
                    if (!isValidTile (dx, dy, lx, ly))
1316
0
                        THROW (IEX_NAMESPACE::ArgExc,
1317
0
                               "Tile (" << dx << ", " << dy << ", " <<
1318
0
                               lx << "," << ly << ") is not a valid tile.");
1319
1320
0
                    ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1321
0
                                                                  _data,
1322
0
                                                                  tileNumber++,
1323
0
                                                                  dx, dy,
1324
0
                                                                  lx, ly));
1325
0
                }
1326
0
            }
1327
1328
            //
1329
            // finish all tasks
1330
            //
1331
0
        }
1332
1333
        //
1334
        // Exeption handling:
1335
        //
1336
        // TileBufferTask::execute() may have encountered exceptions, but
1337
        // those exceptions occurred in another thread, not in the thread
1338
        // that is executing this call to TiledInputFile::readTiles().
1339
        // TileBufferTask::execute() has caught all exceptions and stored
1340
        // the exceptions' what() strings in the tile buffers.
1341
        // Now we check if any tile buffer contains a stored exception; if
1342
        // this is the case then we re-throw the exception in this thread.
1343
        // (It is possible that multiple tile buffers contain stored
1344
        // exceptions.  We re-throw the first exception we find and
1345
        // ignore all others.)
1346
        //
1347
1348
0
        const string *exception = 0;
1349
1350
0
        for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1351
0
        {
1352
0
            TileBuffer *tileBuffer = _data->tileBuffers[i];
1353
1354
0
            if (tileBuffer->hasException && !exception)
1355
0
                exception = &tileBuffer->exception;
1356
1357
0
            tileBuffer->hasException = false;
1358
0
        }
1359
1360
0
        if (exception)
1361
0
            throw IEX_NAMESPACE::IoExc (*exception);
1362
0
    }
1363
0
    catch (IEX_NAMESPACE::BaseExc &e)
1364
0
    {
1365
0
        REPLACE_EXC (e, "Error reading pixel data from image "
1366
0
                        "file \"" << fileName() << "\". " << e);
1367
0
        throw;
1368
0
    }
1369
0
}
1370
1371
1372
void
1373
DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1374
0
{
1375
0
    readTiles (dx1, dx2, dy1, dy2, l, l);
1376
0
}
1377
1378
1379
void
1380
DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly)
1381
0
{
1382
0
    readTiles (dx, dx, dy, dy, lx, ly);
1383
0
}
1384
1385
1386
void
1387
DeepTiledInputFile::readTile (int dx, int dy, int l)
1388
0
{
1389
0
    readTile (dx, dy, l, l);
1390
0
}
1391
1392
1393
void
1394
DeepTiledInputFile::rawTileData (int &dx, int &dy,
1395
                             int &lx, int &ly,
1396
                             char * pixelData,
1397
                             Int64 &pixelDataSize) const
1398
0
{
1399
0
     if (!isValidTile (dx, dy, lx, ly))
1400
0
               throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1401
0
                                   "the image file's data window.");
1402
    
1403
0
     Int64 tileOffset = _data->tileOffsets (dx, dy, lx, ly);
1404
                                   
1405
0
     if(tileOffset == 0)
1406
0
     {
1407
0
        THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
1408
0
        lx << ", " << ly << ") is missing.");
1409
0
     }
1410
     
1411
0
     Lock lock(*_data->_streamData);
1412
                                   
1413
0
     if (_data->_streamData->is->tellg() != tileOffset)
1414
0
                                          _data->_streamData->is->seekg (tileOffset);
1415
                                   
1416
     
1417
     //
1418
     // Read the first few bytes of the tile (the header).
1419
     // Verify that the tile coordinates and the level number
1420
     // are correct.
1421
     //
1422
     
1423
0
     int tileXCoord, tileYCoord, levelX, levelY;
1424
     
1425
0
     if (isMultiPart(_data->version))
1426
0
     {
1427
0
         int partNumber;
1428
0
         Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1429
0
         if (partNumber != _data->partNumber)
1430
0
         {
1431
0
             THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
1432
0
             << ", should be " << _data->partNumber << ".");
1433
0
         }
1434
0
     }
1435
     
1436
0
     Xdr::read <StreamIO> (*_data->_streamData->is, tileXCoord);
1437
0
     Xdr::read <StreamIO> (*_data->_streamData->is, tileYCoord);
1438
0
     Xdr::read <StreamIO> (*_data->_streamData->is, levelX);
1439
0
     Xdr::read <StreamIO> (*_data->_streamData->is, levelY);
1440
     
1441
0
     Int64 sampleCountTableSize;
1442
0
     Int64 packedDataSize;
1443
0
     Xdr::read <StreamIO> (*_data->_streamData->is, sampleCountTableSize);
1444
     
1445
0
     Xdr::read <StreamIO> (*_data->_streamData->is, packedDataSize);
1446
     
1447
          
1448
     
1449
0
     if (tileXCoord != dx)
1450
0
         throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1451
     
1452
0
     if (tileYCoord != dy)
1453
0
         throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1454
     
1455
0
     if (levelX != lx)
1456
0
         throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1457
     
1458
0
     if (levelY != ly)
1459
0
         throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1460
     
1461
     
1462
     // total requirement for reading all the data
1463
     
1464
0
     Int64 totalSizeRequired=40+sampleCountTableSize+packedDataSize;
1465
     
1466
0
     bool big_enough = totalSizeRequired<=pixelDataSize;
1467
     
1468
0
     pixelDataSize = totalSizeRequired;
1469
     
1470
     // was the block we were given big enough?
1471
0
     if(!big_enough || pixelData==NULL)
1472
0
     {        
1473
         // special case: seek stream back to start if we are at the beginning (regular reading pixels assumes it doesn't need to seek
1474
         // in single part files)
1475
0
         if(!isMultiPart(_data->version))
1476
0
         {
1477
0
             _data->_streamData->is->seekg(_data->_streamData->currentPosition); 
1478
0
         }
1479
         // leave lock here - bail before reading more data
1480
0
         return;
1481
0
     }
1482
     
1483
     // copy the values we have read into the output block
1484
0
     *(int *) (pixelData+0) = dx;
1485
0
     *(int *) (pixelData+4) = dy;
1486
0
     *(int *) (pixelData+8) = levelX;
1487
0
     *(int *) (pixelData+12) = levelY;
1488
0
     *(Int64 *) (pixelData+16) =sampleCountTableSize;
1489
0
     *(Int64 *) (pixelData+24) = packedDataSize;
1490
     
1491
     // didn't read the unpackedsize - do that now
1492
0
     Xdr::read<StreamIO> (*_data->_streamData->is, *(Int64 *) (pixelData+32));
1493
     
1494
     // read the actual data
1495
0
     _data->_streamData->is->read(pixelData+40, sampleCountTableSize+packedDataSize);
1496
     
1497
     
1498
0
     if(!isMultiPart(_data->version))
1499
0
     {
1500
0
         _data->_streamData->currentPosition+=sampleCountTableSize+packedDataSize+40;
1501
0
     }
1502
     
1503
     // leave lock here
1504
     
1505
     
1506
0
}
1507
1508
1509
unsigned int
1510
DeepTiledInputFile::tileXSize () const
1511
0
{
1512
0
    return _data->tileDesc.xSize;
1513
0
}
1514
1515
1516
unsigned int
1517
DeepTiledInputFile::tileYSize () const
1518
0
{
1519
0
    return _data->tileDesc.ySize;
1520
0
}
1521
1522
1523
LevelMode
1524
DeepTiledInputFile::levelMode () const
1525
0
{
1526
0
    return _data->tileDesc.mode;
1527
0
}
1528
1529
1530
LevelRoundingMode
1531
DeepTiledInputFile::levelRoundingMode () const
1532
0
{
1533
0
    return _data->tileDesc.roundingMode;
1534
0
}
1535
1536
1537
int
1538
DeepTiledInputFile::numLevels () const
1539
0
{
1540
0
    if (levelMode() == RIPMAP_LEVELS)
1541
0
        THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1542
0
                              "file \"" << fileName() << "\" "
1543
0
                              "(numLevels() is not defined for files "
1544
0
                              "with RIPMAP level mode).");
1545
1546
0
    return _data->numXLevels;
1547
0
}
1548
1549
1550
int
1551
DeepTiledInputFile::numXLevels () const
1552
0
{
1553
0
    return _data->numXLevels;
1554
0
}
1555
1556
1557
int
1558
DeepTiledInputFile::numYLevels () const
1559
0
{
1560
0
    return _data->numYLevels;
1561
0
}
1562
1563
1564
bool
1565
DeepTiledInputFile::isValidLevel (int lx, int ly) const
1566
0
{
1567
0
    if (lx < 0 || ly < 0)
1568
0
        return false;
1569
1570
0
    if (levelMode() == MIPMAP_LEVELS && lx != ly)
1571
0
        return false;
1572
1573
0
    if (lx >= numXLevels() || ly >= numYLevels())
1574
0
        return false;
1575
1576
0
    return true;
1577
0
}
1578
1579
1580
int
1581
DeepTiledInputFile::levelWidth (int lx) const
1582
0
{
1583
0
    try
1584
0
    {
1585
0
        return levelSize (_data->minX, _data->maxX, lx,
1586
0
                          _data->tileDesc.roundingMode);
1587
0
    }
1588
0
    catch (IEX_NAMESPACE::BaseExc &e)
1589
0
    {
1590
0
        REPLACE_EXC (e, "Error calling levelWidth() on image "
1591
0
                        "file \"" << fileName() << "\". " << e);
1592
0
        throw;
1593
0
    }
1594
0
}
1595
1596
1597
int
1598
DeepTiledInputFile::levelHeight (int ly) const
1599
0
{
1600
0
    try
1601
0
    {
1602
0
        return levelSize (_data->minY, _data->maxY, ly,
1603
0
                          _data->tileDesc.roundingMode);
1604
0
    }
1605
0
    catch (IEX_NAMESPACE::BaseExc &e)
1606
0
    {
1607
0
        REPLACE_EXC (e, "Error calling levelHeight() on image "
1608
0
                        "file \"" << fileName() << "\". " << e);
1609
0
        throw;
1610
0
    }
1611
0
}
1612
1613
1614
int
1615
DeepTiledInputFile::numXTiles (int lx) const
1616
0
{
1617
0
    if (lx < 0 || lx >= _data->numXLevels)
1618
0
    {
1619
0
        THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1620
0
                            "file \"" << _data->_streamData->is->fileName() << "\" "
1621
0
                            "(Argument is not in valid range).");
1622
1623
0
    }
1624
1625
0
    return _data->numXTiles[lx];
1626
0
}
1627
1628
1629
int
1630
DeepTiledInputFile::numYTiles (int ly) const
1631
0
{
1632
0
    if (ly < 0 || ly >= _data->numYLevels)
1633
0
    {
1634
0
        THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1635
0
                            "file \"" << _data->_streamData->is->fileName() << "\" "
1636
0
                            "(Argument is not in valid range).");
1637
0
    }
1638
1639
0
    return _data->numYTiles[ly];
1640
0
}
1641
1642
1643
Box2i
1644
DeepTiledInputFile::dataWindowForLevel (int l) const
1645
0
{
1646
0
    return dataWindowForLevel (l, l);
1647
0
}
1648
1649
1650
Box2i
1651
DeepTiledInputFile::dataWindowForLevel (int lx, int ly) const
1652
0
{
1653
0
    try
1654
0
    {
1655
0
        return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1656
0
                _data->tileDesc,
1657
0
                _data->minX, _data->maxX,
1658
0
                _data->minY, _data->maxY,
1659
0
                lx, ly);
1660
0
    }
1661
0
    catch (IEX_NAMESPACE::BaseExc &e)
1662
0
    {
1663
0
        REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1664
0
                        "file \"" << fileName() << "\". " << e);
1665
0
        throw;
1666
0
    }
1667
0
}
1668
1669
1670
Box2i
1671
DeepTiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1672
0
{
1673
0
    return dataWindowForTile (dx, dy, l, l);
1674
0
}
1675
1676
1677
Box2i
1678
DeepTiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1679
0
{
1680
0
    try
1681
0
    {
1682
0
        if (!isValidTile (dx, dy, lx, ly))
1683
0
            throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1684
1685
0
        return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1686
0
                _data->tileDesc,
1687
0
                _data->minX, _data->maxX,
1688
0
                _data->minY, _data->maxY,
1689
0
                dx, dy, lx, ly);
1690
0
    }
1691
0
    catch (IEX_NAMESPACE::BaseExc &e)
1692
0
    {
1693
0
        REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1694
0
                        "file \"" << fileName() << "\". " << e);
1695
0
        throw;
1696
0
    }
1697
0
}
1698
1699
1700
bool
1701
DeepTiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1702
0
{
1703
0
    return ((lx < _data->numXLevels && lx >= 0) &&
1704
0
            (ly < _data->numYLevels && ly >= 0) &&
1705
0
            (dx < _data->numXTiles[lx] && dx >= 0) &&
1706
0
            (dy < _data->numYTiles[ly] && dy >= 0));
1707
0
}
1708
1709
1710
void
1711
DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1712
                                           int dy1, int dy2,
1713
                                           int lx,  int ly)
1714
0
{
1715
0
    Int64 savedFilePos = 0;
1716
1717
0
    try
1718
0
    {
1719
0
        Lock lock (*_data->_streamData);
1720
1721
0
        savedFilePos = _data->_streamData->is->tellg();
1722
1723
        
1724
0
        if (!isValidLevel (lx, ly))
1725
0
        {
1726
0
            THROW (IEX_NAMESPACE::ArgExc,
1727
0
                   "Level coordinate "
1728
0
                   "(" << lx << ", " << ly << ") "
1729
0
                   "is invalid.");
1730
0
        }
1731
        
1732
0
        if (dx1 > dx2)
1733
0
            std::swap (dx1, dx2);
1734
1735
0
        if (dy1 > dy2)
1736
0
            std::swap (dy1, dy2);
1737
1738
0
        int dyStart = dy1;
1739
0
        int dyStop  = dy2 + 1;
1740
0
        int dY      = 1;
1741
1742
0
        if (_data->lineOrder == DECREASING_Y)
1743
0
        {
1744
0
            dyStart = dy2;
1745
0
            dyStop  = dy1 - 1;
1746
0
            dY      = -1;
1747
0
        }
1748
1749
        // (TODO) Check if we have read the sample counts for those tiles,
1750
        // if we have, no need to read again.
1751
0
        for (int dy = dyStart; dy != dyStop; dy += dY)
1752
0
        {
1753
0
            for (int dx = dx1; dx <= dx2; dx++)
1754
0
            {
1755
                
1756
0
                if (!isValidTile (dx, dy, lx, ly))
1757
0
                {
1758
0
                    THROW (IEX_NAMESPACE::ArgExc,
1759
0
                           "Tile (" << dx << ", " << dy << ", " <<
1760
0
                           lx << "," << ly << ") is not a valid tile.");
1761
0
                }
1762
                
1763
0
                Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1764
0
                        _data->tileDesc,
1765
0
                        _data->minX, _data->maxX,
1766
0
                        _data->minY, _data->maxY,
1767
0
                        dx, dy, lx, ly);
1768
1769
0
                int xOffset = _data->sampleCountXTileCoords * tileRange.min.x;
1770
0
                int yOffset = _data->sampleCountYTileCoords * tileRange.min.y;
1771
1772
                //
1773
                // Skip and check the tile coordinates.
1774
                //
1775
1776
0
                _data->_streamData->is->seekg(_data->tileOffsets(dx, dy, lx, ly));
1777
1778
0
                if (isMultiPart(_data->version))
1779
0
                {
1780
0
                    int partNumber;
1781
0
                    Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1782
1783
0
                    if (partNumber != _data->partNumber)
1784
0
                        throw IEX_NAMESPACE::InputExc ("Unexpected part number.");
1785
0
                }
1786
1787
0
                int xInFile, yInFile, lxInFile, lyInFile;
1788
0
                Xdr::read <StreamIO> (*_data->_streamData->is, xInFile);
1789
0
                Xdr::read <StreamIO> (*_data->_streamData->is, yInFile);
1790
0
                Xdr::read <StreamIO> (*_data->_streamData->is, lxInFile);
1791
0
                Xdr::read <StreamIO> (*_data->_streamData->is, lyInFile);
1792
1793
0
                if (xInFile != dx)
1794
0
                    throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1795
1796
0
                if (yInFile != dy)
1797
0
                    throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1798
1799
0
                if (lxInFile != lx)
1800
0
                    throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1801
1802
0
                if (lyInFile != ly)
1803
0
                    throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1804
1805
0
                Int64 tableSize, dataSize, unpackedDataSize;
1806
0
                Xdr::read <StreamIO> (*_data->_streamData->is, tableSize);
1807
0
                Xdr::read <StreamIO> (*_data->_streamData->is, dataSize);
1808
0
                Xdr::read <StreamIO> (*_data->_streamData->is, unpackedDataSize);
1809
1810
                
1811
0
                if(tableSize>_data->maxSampleCountTableSize)
1812
0
                {
1813
0
                    THROW (IEX_NAMESPACE::ArgExc, "Bad sampleCountTableDataSize read from tile "<< dx << ',' << dy << ',' << lx << ',' << ly << ": expected " << _data->maxSampleCountTableSize << " or less, got "<< tableSize);
1814
0
                }
1815
                    
1816
                
1817
                //
1818
                // We make a check on the data size requirements here.
1819
                // Whilst we wish to store 64bit sizes on disk, not all the compressors
1820
                // have been made to work with such data sizes and are still limited to
1821
                // using signed 32 bit (int) for the data size. As such, this version
1822
                // insists that we validate that the data size does not exceed the data
1823
                // type max limit.
1824
                // @TODO refactor the compressor code to ensure full 64-bit support.
1825
                //
1826
1827
0
                Int64 compressorMaxDataSize = Int64(std::numeric_limits<int>::max());
1828
0
                if (dataSize         > compressorMaxDataSize ||
1829
0
                    unpackedDataSize > compressorMaxDataSize ||
1830
0
                    tableSize        > compressorMaxDataSize)
1831
0
                {
1832
0
                    THROW (IEX_NAMESPACE::ArgExc, "This version of the library does not"
1833
0
                          << "support the allocation of data with size  > "
1834
0
                          << compressorMaxDataSize
1835
0
                          << " file table size    :" << tableSize
1836
0
                          << " file unpacked size :" << unpackedDataSize
1837
0
                          << " file packed size   :" << dataSize << ".\n");
1838
0
                }
1839
1840
                //
1841
                // Read and uncompress the pixel sample count table.
1842
                //
1843
1844
0
                _data->_streamData->is->read(_data->sampleCountTableBuffer, tableSize);
1845
1846
0
                const char* readPtr;
1847
1848
0
                if (tableSize < _data->maxSampleCountTableSize)
1849
0
                {
1850
0
                    if(!_data->sampleCountTableComp)
1851
0
                    {
1852
0
                        THROW(IEX_NAMESPACE::ArgExc,"Deep scanline data corrupt at tile " << dx << ',' << dy << ',' << lx << ',' <<  ly << " (sampleCountTableDataSize error)");
1853
0
                    }
1854
0
                    _data->sampleCountTableComp->uncompress(_data->sampleCountTableBuffer,
1855
0
                                                            tableSize,
1856
0
                                                            tileRange.min.y,
1857
0
                                                            readPtr);
1858
0
                }
1859
0
                else
1860
0
                    readPtr = _data->sampleCountTableBuffer;
1861
1862
0
                size_t cumulative_total_samples =0;
1863
0
                int lastAccumulatedCount;
1864
0
                for (int j = tileRange.min.y; j <= tileRange.max.y; j++)
1865
0
                {
1866
0
                    lastAccumulatedCount = 0;
1867
0
                    for (int i = tileRange.min.x; i <= tileRange.max.x; i++)
1868
0
                    {
1869
0
                        int accumulatedCount;
1870
0
                        Xdr::read <CharPtrIO> (readPtr, accumulatedCount);
1871
                        
1872
0
                        if (accumulatedCount < lastAccumulatedCount)
1873
0
                        {
1874
0
                            THROW(IEX_NAMESPACE::ArgExc,"Deep tile sampleCount data corrupt at tile " 
1875
0
                                  << dx << ',' << dy << ',' << lx << ',' <<  ly << " (negative sample count detected)");
1876
0
                        }
1877
1878
0
                        int count = accumulatedCount - lastAccumulatedCount;
1879
0
                        lastAccumulatedCount = accumulatedCount;
1880
                        
1881
0
                        _data->getSampleCount(i - xOffset, j - yOffset) =count;
1882
0
                    }
1883
0
                    cumulative_total_samples += lastAccumulatedCount;
1884
0
                }
1885
                
1886
0
                if(cumulative_total_samples * _data->combinedSampleSize > unpackedDataSize)
1887
0
                {
1888
0
                    THROW(IEX_NAMESPACE::ArgExc,"Deep scanline sampleCount data corrupt at tile " 
1889
0
                                                << dx << ',' << dy << ',' << lx << ',' <<  ly 
1890
0
                                                << ": pixel data only contains " << unpackedDataSize 
1891
0
                                                << " bytes of data but table references at least " 
1892
0
                                                << cumulative_total_samples*_data->combinedSampleSize << " bytes of sample data" );            
1893
0
                }
1894
                    
1895
0
            }
1896
0
        }
1897
1898
0
        _data->_streamData->is->seekg(savedFilePos);
1899
0
    }
1900
0
    catch (IEX_NAMESPACE::BaseExc &e)
1901
0
    {
1902
0
        REPLACE_EXC (e, "Error reading sample count data from image "
1903
0
                        "file \"" << fileName() << "\". " << e);
1904
1905
0
         _data->_streamData->is->seekg(savedFilePos);
1906
1907
0
        throw;
1908
0
    }
1909
0
}
1910
1911
1912
void
1913
DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int l)
1914
0
{
1915
0
    readPixelSampleCount (dx, dy, l, l);
1916
0
}
1917
1918
1919
void
1920
DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int lx, int ly)
1921
0
{
1922
0
    readPixelSampleCounts (dx, dx, dy, dy, lx, ly);
1923
0
}
1924
1925
1926
void
1927
DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1928
                                           int dy1, int dy2,
1929
                                           int l)
1930
0
{
1931
0
    readPixelSampleCounts (dx1, dx2, dy1, dy2, l, l);
1932
0
}
1933
1934
1935
size_t
1936
DeepTiledInputFile::totalTiles() const
1937
0
{
1938
    //
1939
    // Calculate the total number of tiles in the file
1940
    //
1941
    
1942
0
    int numAllTiles = 0;
1943
    
1944
0
    switch (levelMode ())
1945
0
    {
1946
0
        case ONE_LEVEL:
1947
0
        case MIPMAP_LEVELS:
1948
            
1949
0
            for (int i_l = 0; i_l < numLevels (); ++i_l)
1950
0
                numAllTiles += numXTiles (i_l) * numYTiles (i_l);
1951
            
1952
0
            break;
1953
            
1954
0
        case RIPMAP_LEVELS:
1955
            
1956
0
            for (int i_ly = 0; i_ly < numYLevels (); ++i_ly)
1957
0
                for (int i_lx = 0; i_lx < numXLevels (); ++i_lx)
1958
0
                    numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
1959
                
1960
0
                break;
1961
            
1962
0
        default:
1963
            
1964
0
            throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
1965
0
    }
1966
0
    return numAllTiles;
1967
0
}
1968
1969
1970
1971
1972
void 
1973
DeepTiledInputFile::getTileOrder(int dx[],int dy[],int lx[],int ly[]) const
1974
0
{
1975
0
  return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1976
  
1977
0
}
1978
1979
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT