Coverage Report

Created: 2025-06-13 06:50

/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
0
    typeInFrameBuffer (tifb),
127
0
    typeInFile (tifl),
128
0
    pointerArrayBase (b),
129
0
    xStride (xs),
130
0
    yStride (ys),
131
0
    sampleStride (spst),
132
0
    fill (f),
133
0
    skip (s),
134
0
    fillValue (fv),
135
0
    xTileCoords (xtc),
136
0
    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
0
    uncompressedData (0),
172
0
    buffer (0),
173
0
    dataSize (0),
174
0
    compressor (0),
175
0
    format (defaultFormat (compressor)),
176
0
    dx (-1),
177
0
    dy (-1),
178
0
    lx (-1),
179
0
    ly (-1),
180
0
    hasException (false),
181
0
    exception (),
182
0
    _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
0
    numXTiles (0),
281
0
    numYTiles (0),
282
0
    partNumber (-1),
283
0
    multiPartBackwardSupport(false),
284
0
    numThreads(numThreads),
285
0
    memoryMapped(false),
286
    _streamData(NULL),
287
0
    _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
0
    Task (group),
544
0
    _ifd (ifd),
545
0
    _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
0
    _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
0
    _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
0
    _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
0
    _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