Coverage Report

Created: 2025-06-13 06:50

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