Coverage Report

Created: 2025-07-23 07:06

/src/opencv/3rdparty/openexr/IlmImf/ImfScanLineInputFile.cpp
Line
Count
Source (jump to first uncovered line)
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4
// Digital Ltd. LLC
5
// 
6
// All rights reserved.
7
// 
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
10
// met:
11
// *       Redistributions of source code must retain the above copyright
12
// notice, this list of conditions and the following disclaimer.
13
// *       Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following disclaimer
15
// in the documentation and/or other materials provided with the
16
// distribution.
17
// *       Neither the name of Industrial Light & Magic nor the names of
18
// its contributors may be used to endorse or promote products derived
19
// from this software without specific prior written permission. 
20
// 
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
//
33
///////////////////////////////////////////////////////////////////////////
34
35
36
//-----------------------------------------------------------------------------
37
//
38
//  class ScanLineInputFile
39
//
40
//-----------------------------------------------------------------------------
41
42
#include "ImfScanLineInputFile.h"
43
#include "ImfChannelList.h"
44
#include "ImfMisc.h"
45
#include "ImfStdIO.h"
46
#include "ImfCompressor.h"
47
#include "ImathBox.h"
48
#include "ImathFun.h"
49
#include <ImfXdr.h>
50
#include <ImfConvert.h>
51
#include <ImfThreading.h>
52
#include <ImfPartType.h>
53
#include "IlmThreadPool.h"
54
#include "IlmThreadSemaphore.h"
55
#include "IlmThreadMutex.h"
56
#include "Iex.h"
57
#include "ImfVersion.h"
58
#include "ImfOptimizedPixelReading.h"
59
#include "ImfNamespace.h"
60
#include "ImfStandardAttributes.h"
61
62
#include <algorithm>
63
#include <string>
64
#include <vector>
65
#include <assert.h>
66
#include <cstring>
67
68
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
69
70
71
using IMATH_NAMESPACE::Box2i;
72
using IMATH_NAMESPACE::divp;
73
using IMATH_NAMESPACE::modp;
74
using std::string;
75
using std::vector;
76
using std::ifstream;
77
using std::min;
78
using std::max;
79
using std::sort;
80
using ILMTHREAD_NAMESPACE::Mutex;
81
using ILMTHREAD_NAMESPACE::Lock;
82
using ILMTHREAD_NAMESPACE::Semaphore;
83
using ILMTHREAD_NAMESPACE::Task;
84
using ILMTHREAD_NAMESPACE::TaskGroup;
85
using ILMTHREAD_NAMESPACE::ThreadPool;
86
87
namespace {
88
89
struct InSliceInfo
90
{
91
    PixelType typeInFrameBuffer;
92
    PixelType typeInFile;
93
    char *  base;
94
    size_t  xStride;
95
    size_t  yStride;
96
    int   xSampling;
97
    int   ySampling;
98
    bool  fill;
99
    bool  skip;
100
    double  fillValue;
101
102
    InSliceInfo (PixelType typeInFrameBuffer = HALF,
103
     PixelType typeInFile = HALF,
104
           char *base = 0,
105
           size_t xStride = 0,
106
           size_t yStride = 0,
107
           int xSampling = 1,
108
           int ySampling = 1,
109
           bool fill = false,
110
           bool skip = false,
111
           double fillValue = 0.0);
112
};
113
114
115
InSliceInfo::InSliceInfo (PixelType tifb,
116
        PixelType tifl,
117
        char *b,
118
        size_t xs, size_t ys,
119
        int xsm, int ysm,
120
        bool f, bool s,
121
        double fv)
122
:
123
0
    typeInFrameBuffer (tifb),
124
0
    typeInFile (tifl),
125
0
    base (b),
126
0
    xStride (xs),
127
0
    yStride (ys),
128
0
    xSampling (xsm),
129
0
    ySampling (ysm),
130
0
    fill (f),
131
0
    skip (s),
132
0
    fillValue (fv)
133
0
{
134
    // empty
135
0
}
136
137
138
struct LineBuffer
139
{
140
    const char *  uncompressedData;
141
    char *    buffer;
142
    int     dataSize;
143
    int     minY;
144
    int     maxY;
145
    Compressor *  compressor;
146
    Compressor::Format  format;
147
    int     number;
148
    bool    hasException;
149
    string    exception;
150
151
    LineBuffer (Compressor * const comp);
152
    ~LineBuffer ();
153
154
0
    inline void   wait () {_sem.wait();}
155
0
    inline void   post () {_sem.post();}
156
157
  private:
158
159
    Semaphore   _sem;
160
};
161
162
163
LineBuffer::LineBuffer (Compressor *comp):
164
0
    uncompressedData (0),
165
0
    buffer (0),
166
0
    dataSize (0),
167
0
    compressor (comp),
168
0
    format (defaultFormat(compressor)),
169
0
    number (-1),
170
0
    hasException (false),
171
0
    exception (),
172
0
    _sem (1)
173
0
{
174
    // empty
175
0
}
176
177
178
LineBuffer::~LineBuffer ()
179
0
{
180
0
    delete compressor;
181
0
}
182
183
/// helper struct used to detect the order that the channels are stored
184
185
struct sliceOptimizationData
186
{
187
    const char * base;   ///< pointer to pixel data 
188
    bool fill;           ///< is this channel being filled with constant, instead of read?
189
    half fillValue;      ///< if filling, the value to use
190
    size_t offset;       ///< position this channel will be in the read buffer, accounting for previous channels, as well as their type
191
    PixelType type;      ///< type of channel
192
    size_t xStride;      ///< x-stride of channel in buffer (must be set to cause channels to interleave)
193
    size_t yStride;      ///< y-stride of channel in buffer (must be same in all channels, else order will change, which is bad)
194
    int xSampling;       ///< channel x sampling
195
    int ySampling;       ///< channel y sampling
196
            
197
            
198
    /// we need to keep the list sorted in the order they'll be written to memory
199
    bool operator<(const sliceOptimizationData& other ) const
200
0
    {
201
0
        return base < other.base;
202
0
    }
203
};
204
    
205
206
} // namespace
207
208
209
struct ScanLineInputFile::Data: public Mutex
210
{
211
    Header    header;       // the image header
212
    int     version;            // file's version
213
    FrameBuffer   frameBuffer;      // framebuffer to write into
214
    LineOrder   lineOrder;          // order of the scanlines in file
215
    int     minX;       // data window's min x coord
216
    int     maxX;       // data window's max x coord
217
    int     minY;       // data window's min y coord
218
    int     maxY;       // data window's max x coord
219
    vector<Int64> lineOffsets;      // stores offsets in file for
220
              // each line
221
    bool    fileIsComplete;     // True if no scanlines are missing
222
                  // in the file
223
    int     nextLineBufferMinY; // minimum y of the next linebuffer
224
    vector<size_t>  bytesPerLine;       // combined size of a line over all
225
                                            // channels
226
    vector<size_t>  offsetInLineBuffer; // offset for each scanline in its
227
                                            // linebuffer
228
    vector<InSliceInfo> slices;             // info about channels in file
229
    
230
    vector<LineBuffer*> lineBuffers;        // each holds one line buffer
231
    int     linesInBuffer;      // number of scanlines each buffer
232
                                            // holds
233
    size_t    lineBufferSize;     // size of the line buffer
234
    int                 partNumber;         // part number
235
236
    bool                memoryMapped;       // if the stream is memory mapped
237
    OptimizationMode    optimizationMode;   // optimizibility of the input file
238
    vector<sliceOptimizationData>  optimizationData; ///< channel ordering for optimized reading
239
    
240
    Data (int numThreads);
241
    ~Data ();
242
    
243
    inline LineBuffer * getLineBuffer (int number); // hash function from line
244
                    // buffer indices into our
245
                // vector of line buffers
246
    
247
    
248
};
249
250
251
ScanLineInputFile::Data::Data (int numThreads):
252
0
        partNumber(-1),
253
0
        memoryMapped(false)
254
0
{
255
    //
256
    // We need at least one lineBuffer, but if threading is used,
257
    // to keep n threads busy we need 2*n lineBuffers
258
    //
259
260
0
    lineBuffers.resize (max (1, 2 * numThreads));
261
0
}
262
263
264
ScanLineInputFile::Data::~Data ()
265
0
{
266
0
    for (size_t i = 0; i < lineBuffers.size(); i++)
267
0
        delete lineBuffers[i];
268
0
}
269
270
271
inline LineBuffer *
272
ScanLineInputFile::Data::getLineBuffer (int lineBufferNumber)
273
0
{
274
0
    return lineBuffers[lineBufferNumber % lineBuffers.size()];
275
0
}
276
277
278
namespace {
279
280
281
void
282
reconstructLineOffsets (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,
283
      LineOrder lineOrder,
284
      vector<Int64> &lineOffsets)
285
0
{
286
0
    Int64 position = is.tellg();
287
288
0
    try
289
0
    {
290
0
  for (unsigned int i = 0; i < lineOffsets.size(); i++)
291
0
  {
292
0
      Int64 lineOffset = is.tellg();
293
294
0
      int y;
295
0
      OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, y);
296
297
0
      int dataSize;
298
0
      OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, dataSize);
299
300
0
      Xdr::skip <StreamIO> (is, dataSize);
301
302
0
      if (lineOrder == INCREASING_Y)
303
0
    lineOffsets[i] = lineOffset;
304
0
      else
305
0
    lineOffsets[lineOffsets.size() - i - 1] = lineOffset;
306
0
  }
307
0
    }
308
0
    catch (...)
309
0
    {
310
  //
311
  // Suppress all exceptions.  This functions is
312
  // called only to reconstruct the line offset
313
  // table for incomplete files, and exceptions
314
  // are likely.
315
  //
316
0
    }
317
318
0
    is.clear();
319
0
    is.seekg (position);
320
0
}
321
322
323
void
324
readLineOffsets (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,
325
     LineOrder lineOrder,
326
     vector<Int64> &lineOffsets,
327
     bool &complete)
328
0
{
329
0
    for (unsigned int i = 0; i < lineOffsets.size(); i++)
330
0
    {
331
0
  OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (is, lineOffsets[i]);
332
0
    }
333
334
0
    complete = true;
335
336
0
    for (unsigned int i = 0; i < lineOffsets.size(); i++)
337
0
    {
338
0
  if (lineOffsets[i] <= 0)
339
0
  {
340
      //
341
      // Invalid data in the line offset table mean that
342
      // the file is probably incomplete (the table is
343
      // the last thing written to the file).  Either
344
      // some process is still busy writing the file,
345
      // or writing the file was aborted.
346
      //
347
      // We should still be able to read the existing
348
      // parts of the file.  In order to do this, we
349
      // have to make a sequential scan over the scan
350
      // line data to reconstruct the line offset table.
351
      //
352
353
0
      complete = false;
354
0
      reconstructLineOffsets (is, lineOrder, lineOffsets);
355
0
      break;
356
0
  }
357
0
    }
358
0
}
359
360
361
void
362
readPixelData (InputStreamMutex *streamData,
363
               ScanLineInputFile::Data *ifd,
364
         int minY,
365
         char *&buffer,
366
         int &dataSize)
367
0
{
368
    //
369
    // Read a single line buffer from the input file.
370
    //
371
    // If the input file is not memory-mapped, we copy the pixel data into
372
    // into the array pointed to by buffer.  If the file is memory-mapped,
373
    // then we change where buffer points to instead of writing into the
374
    // array (hence buffer needs to be a reference to a char *).
375
    //
376
377
0
    int lineBufferNumber = (minY - ifd->minY) / ifd->linesInBuffer;
378
0
    if (lineBufferNumber < 0 || lineBufferNumber >= int(ifd->lineOffsets.size()))
379
0
        THROW (IEX_NAMESPACE::InputExc, "Invalid scan line " << minY << " requested or missing.");
380
381
0
    Int64 lineOffset = ifd->lineOffsets[lineBufferNumber];
382
383
0
    if (lineOffset == 0)
384
0
  THROW (IEX_NAMESPACE::InputExc, "Scan line " << minY << " is missing.");
385
386
    //
387
    // Seek to the start of the scan line in the file,
388
    // if necessary.
389
    //
390
391
0
    if ( !isMultiPart(ifd->version) )
392
0
    {
393
0
        if (ifd->nextLineBufferMinY != minY)
394
0
            streamData->is->seekg (lineOffset);
395
0
    }
396
0
    else
397
0
    {
398
        //
399
        // In a multi-part file, the file pointer may have been moved by
400
        // other parts, so we have to ask tellg() where we are.
401
        //
402
0
        if (streamData->is->tellg() != ifd->lineOffsets[lineBufferNumber])
403
0
            streamData->is->seekg (lineOffset);
404
0
    }
405
406
    //
407
    // Read the data block's header.
408
    //
409
410
0
    int yInFile;
411
412
    //
413
    // Read the part number when we are dealing with a multi-part file.
414
    //
415
0
    if (isMultiPart(ifd->version))
416
0
    {
417
0
        int partNumber;
418
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, partNumber);
419
0
        if (partNumber != ifd->partNumber)
420
0
        {
421
0
            THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
422
0
                   << ", should be " << ifd->partNumber << ".");
423
0
        }
424
0
    }
425
426
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, yInFile);
427
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
428
    
429
0
    if (yInFile != minY)
430
0
        throw IEX_NAMESPACE::InputExc ("Unexpected data block y coordinate.");
431
432
0
    if (dataSize > (int) ifd->lineBufferSize)
433
0
  throw IEX_NAMESPACE::InputExc ("Unexpected data block length.");
434
435
    //
436
    // Read the pixel data.
437
    //
438
439
0
    if (streamData->is->isMemoryMapped ())
440
0
        buffer = streamData->is->readMemoryMapped (dataSize);
441
0
    else
442
0
        streamData->is->read (buffer, dataSize);
443
444
    //
445
    // Keep track of which scan line is the next one in
446
    // the file, so that we can avoid redundant seekg()
447
    // operations (seekg() can be fairly expensive).
448
    //
449
450
0
    if (ifd->lineOrder == INCREASING_Y)
451
0
        ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
452
0
    else
453
0
        ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
454
0
}
455
456
                        
457
458
//
459
// A LineBufferTask encapsulates the task uncompressing a set of
460
// scanlines (line buffer) and copying them into the frame buffer.
461
//
462
463
class LineBufferTask : public Task
464
{
465
  public:
466
467
    LineBufferTask (TaskGroup *group,
468
                    ScanLineInputFile::Data *ifd,
469
        LineBuffer *lineBuffer,
470
                    int scanLineMin,
471
        int scanLineMax,
472
                    OptimizationMode optimizationMode);
473
474
    virtual ~LineBufferTask ();
475
476
    virtual void    execute ();
477
478
  private:
479
480
    ScanLineInputFile::Data * _ifd;
481
    LineBuffer *    _lineBuffer;
482
    int       _scanLineMin;
483
    int       _scanLineMax;
484
    OptimizationMode            _optimizationMode;
485
};
486
487
488
LineBufferTask::LineBufferTask
489
    (TaskGroup *group,
490
     ScanLineInputFile::Data *ifd,
491
     LineBuffer *lineBuffer,
492
     int scanLineMin,
493
     int scanLineMax,OptimizationMode optimizationMode)
494
:
495
0
    Task (group),
496
0
    _ifd (ifd),
497
0
    _lineBuffer (lineBuffer),
498
0
    _scanLineMin (scanLineMin),
499
0
    _scanLineMax (scanLineMax),
500
0
    _optimizationMode(optimizationMode)
501
0
{
502
    // empty
503
0
}
504
505
506
LineBufferTask::~LineBufferTask ()
507
0
{
508
    //
509
    // Signal that the line buffer is now free
510
    //
511
512
0
    _lineBuffer->post ();
513
0
}
514
515
516
void
517
LineBufferTask::execute ()
518
0
{
519
0
    try
520
0
    {
521
        //
522
        // Uncompress the data, if necessary
523
        //
524
    
525
0
        if (_lineBuffer->uncompressedData == 0)
526
0
        {
527
0
            int uncompressedSize = 0;
528
0
            int maxY = min (_lineBuffer->maxY, _ifd->maxY);
529
    
530
0
            for (int i = _lineBuffer->minY - _ifd->minY;
531
0
                 i <= maxY - _ifd->minY;
532
0
     ++i)
533
0
      {
534
0
                uncompressedSize += (int) _ifd->bytesPerLine[i];
535
0
      }
536
    
537
0
            if (_lineBuffer->compressor &&
538
0
                _lineBuffer->dataSize < uncompressedSize)
539
0
            {
540
0
                _lineBuffer->format = _lineBuffer->compressor->format();
541
542
0
                _lineBuffer->dataSize = _lineBuffer->compressor->uncompress
543
0
                    (_lineBuffer->buffer,
544
0
                     _lineBuffer->dataSize,
545
0
         _lineBuffer->minY,
546
0
                     _lineBuffer->uncompressedData);
547
0
            }
548
0
            else
549
0
            {
550
                //
551
                // If the line is uncompressed, it's in XDR format,
552
                // regardless of the compressor's output format.
553
                //
554
    
555
0
                _lineBuffer->format = Compressor::XDR;
556
0
                _lineBuffer->uncompressedData = _lineBuffer->buffer;
557
0
            }
558
0
        }
559
        
560
0
        int yStart, yStop, dy;
561
562
0
        if (_ifd->lineOrder == INCREASING_Y)
563
0
        {
564
0
            yStart = _scanLineMin;
565
0
            yStop = _scanLineMax + 1;
566
0
            dy = 1;
567
0
        }
568
0
        else
569
0
        {
570
0
            yStart = _scanLineMax;
571
0
            yStop = _scanLineMin - 1;
572
0
            dy = -1;
573
0
        }
574
    
575
0
        for (int y = yStart; y != yStop; y += dy)
576
0
        {
577
            //
578
            // Convert one scan line's worth of pixel data back
579
            // from the machine-independent representation, and
580
            // store the result in the frame buffer.
581
            //
582
    
583
0
            const char *readPtr = _lineBuffer->uncompressedData +
584
0
                                  _ifd->offsetInLineBuffer[y - _ifd->minY];
585
    
586
            //
587
            // Iterate over all image channels.
588
            //
589
    
590
0
            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
591
0
            {
592
                //
593
                // Test if scan line y of this channel contains any data
594
    // (the scan line contains data only if y % ySampling == 0).
595
                //
596
    
597
0
                const InSliceInfo &slice = _ifd->slices[i];
598
    
599
0
                if (modp (y, slice.ySampling) != 0)
600
0
                    continue;
601
    
602
                //
603
                // Find the x coordinates of the leftmost and rightmost
604
                // sampled pixels (i.e. pixels within the data window
605
                // for which x % xSampling == 0).
606
                //
607
    
608
0
                int dMinX = divp (_ifd->minX, slice.xSampling);
609
0
                int dMaxX = divp (_ifd->maxX, slice.xSampling);
610
    
611
                //
612
    // Fill the frame buffer with pixel data.
613
                //
614
    
615
0
                if (slice.skip)
616
0
                {
617
                    //
618
                    // The file contains data for this channel, but
619
                    // the frame buffer contains no slice for this channel.
620
                    //
621
    
622
0
                    skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1);
623
0
                }
624
0
                else
625
0
                {
626
                    //
627
                    // The frame buffer contains a slice for this channel.
628
                    //
629
    
630
0
                    char *linePtr  = slice.base +
631
0
                                        divp (y, slice.ySampling) *
632
0
                                        slice.yStride;
633
    
634
0
                    char *writePtr = linePtr + dMinX * slice.xStride;
635
0
                    char *endPtr   = linePtr + dMaxX * slice.xStride;
636
                    
637
0
                    copyIntoFrameBuffer (readPtr, writePtr, endPtr,
638
0
                                         slice.xStride, slice.fill,
639
0
                                         slice.fillValue, _lineBuffer->format,
640
0
                                         slice.typeInFrameBuffer,
641
0
                                         slice.typeInFile);
642
0
                }
643
0
            }
644
0
        }
645
0
    }
646
0
    catch (std::exception &e)
647
0
    {
648
0
        if (!_lineBuffer->hasException)
649
0
        {
650
0
            _lineBuffer->exception = e.what();
651
0
            _lineBuffer->hasException = true;
652
0
        }
653
0
    }
654
0
    catch (...)
655
0
    {
656
0
        if (!_lineBuffer->hasException)
657
0
        {
658
0
            _lineBuffer->exception = "unrecognized exception";
659
0
            _lineBuffer->hasException = true;
660
0
        }
661
0
    }
662
0
}
663
664
665
#ifdef IMF_HAVE_SSE2
666
//
667
// IIF format is more restricted than a perfectly generic one,
668
// so it is possible to perform some optimizations.
669
//
670
class LineBufferTaskIIF : public Task
671
{
672
    public:
673
        
674
        LineBufferTaskIIF (TaskGroup *group,
675
                           ScanLineInputFile::Data *ifd,
676
                           LineBuffer *lineBuffer,
677
                           int scanLineMin,
678
                           int scanLineMax,
679
                           OptimizationMode optimizationMode);
680
                           
681
        virtual ~LineBufferTaskIIF ();
682
                           
683
        virtual void                execute ();
684
        
685
        template<typename TYPE>
686
        void getWritePointer (int y,
687
                              unsigned short*& pOutWritePointerRight,
688
                              size_t& outPixelsToCopySSE,
689
                              size_t& outPixelsToCopyNormal,int bank=0) const;
690
                              
691
        template<typename TYPE>
692
        void getWritePointerStereo (int y,
693
                                    unsigned short*& outWritePointerRight,
694
                                    unsigned short*& outWritePointerLeft,
695
                                    size_t& outPixelsToCopySSE,
696
                                    size_t& outPixelsToCopyNormal) const;
697
698
    private:
699
        
700
        ScanLineInputFile::Data *   _ifd;
701
        LineBuffer *                _lineBuffer;
702
        int                         _scanLineMin;
703
        int                         _scanLineMax;
704
        OptimizationMode            _optimizationMode;
705
  
706
};
707
708
LineBufferTaskIIF::LineBufferTaskIIF
709
    (TaskGroup *group,
710
     ScanLineInputFile::Data *ifd,
711
     LineBuffer *lineBuffer,
712
     int scanLineMin,
713
     int scanLineMax,
714
     OptimizationMode optimizationMode
715
    )
716
    :
717
0
     Task (group),
718
0
     _ifd (ifd),
719
0
     _lineBuffer (lineBuffer),
720
0
     _scanLineMin (scanLineMin),
721
0
     _scanLineMax (scanLineMax),
722
0
     _optimizationMode (optimizationMode)
723
0
{
724
     /*
725
     //
726
     // indicates the optimised path has been taken
727
     //
728
     static bool could_optimise=false;
729
     if(could_optimise==false)
730
     {
731
         std::cerr << " optimised path\n";
732
         could_optimise=true;
733
     }
734
     */
735
0
}
736
 
737
LineBufferTaskIIF::~LineBufferTaskIIF ()
738
0
{
739
     //
740
     // Signal that the line buffer is now free
741
     //
742
     
743
0
     _lineBuffer->post ();
744
0
}
745
 
746
// Return 0 if we are to skip because of sampling
747
// channelBank is 0 for the first group of channels, 1 for the second
748
template<typename TYPE>
749
void LineBufferTaskIIF::getWritePointer 
750
                            (int y,
751
                             unsigned short*& outWritePointerRight,
752
                             size_t& outPixelsToCopySSE,
753
                             size_t& outPixelsToCopyNormal,
754
                             int channelBank
755
                            ) const
756
0
{
757
      // Channels are saved alphabetically, so the order is B G R.
758
      // The last slice (R) will give us the location of our write pointer.
759
      // The only slice that we support skipping is alpha, i.e. the first one.  
760
      // This does not impact the write pointer or the pixels to copy at all.
761
      
762
0
      size_t nbSlicesInBank = _ifd->optimizationData.size();
763
      
764
0
      int sizeOfSingleValue = sizeof(TYPE);
765
      
766
0
      if(_ifd->optimizationData.size()>4)
767
0
      {
768
          // there are two banks - we only copy one at once
769
0
          nbSlicesInBank/=2;
770
0
      }
771
772
      
773
0
      size_t firstChannel = 0;
774
0
      if(channelBank==1)
775
0
      {
776
0
          firstChannel = _ifd->optimizationData.size()/2;
777
0
      }
778
      
779
0
       sliceOptimizationData& firstSlice = _ifd->optimizationData[firstChannel];
780
      
781
0
      if (modp (y, firstSlice.ySampling) != 0)
782
0
      {
783
0
          outPixelsToCopySSE    = 0;
784
0
          outPixelsToCopyNormal = 0;
785
0
          outWritePointerRight  = 0;
786
0
      }
787
      
788
0
      const char* linePtr1  = firstSlice.base +
789
0
      divp (y, firstSlice.ySampling) *
790
0
      firstSlice.yStride;
791
      
792
0
      int dMinX1 = divp (_ifd->minX, firstSlice.xSampling);
793
0
      int dMaxX1 = divp (_ifd->maxX, firstSlice.xSampling);
794
      
795
      // Construct the writePtr so that we start writing at
796
      // linePtr + Min offset in the line.
797
0
      outWritePointerRight =  (unsigned short*)(linePtr1 +
798
0
      dMinX1 * firstSlice.xStride );
799
      
800
0
      size_t bytesToCopy  = ((linePtr1 + dMaxX1 * firstSlice.xStride ) -
801
0
      (linePtr1 + dMinX1 * firstSlice.xStride )) + 2;
802
0
      size_t shortsToCopy = bytesToCopy / sizeOfSingleValue;
803
0
      size_t pixelsToCopy = (shortsToCopy / nbSlicesInBank ) + 1;
804
      
805
      // We only support writing to SSE if we have no pixels to copy normally
806
0
      outPixelsToCopySSE    = pixelsToCopy / 8;
807
0
      outPixelsToCopyNormal = pixelsToCopy % 8;
808
      
809
0
}
810
811
812
template<typename TYPE>
813
void LineBufferTaskIIF::getWritePointerStereo 
814
                          (int y,
815
                           unsigned short*& outWritePointerRight,
816
                           unsigned short*& outWritePointerLeft,
817
                           size_t& outPixelsToCopySSE,
818
                           size_t& outPixelsToCopyNormal) const
819
0
{
820
0
   getWritePointer<TYPE>(y,outWritePointerRight,outPixelsToCopySSE,outPixelsToCopyNormal,0);
821
   
822
   
823
0
   if(outWritePointerRight)
824
0
   {
825
0
       getWritePointer<TYPE>(y,outWritePointerLeft,outPixelsToCopySSE,outPixelsToCopyNormal,1);
826
0
   }
827
   
828
0
}
829
830
void
831
LineBufferTaskIIF::execute()
832
0
{
833
0
    try
834
0
    {
835
        //
836
        // Uncompress the data, if necessary
837
        //
838
        
839
0
        if (_lineBuffer->uncompressedData == 0)
840
0
        {
841
0
            int uncompressedSize = 0;
842
0
            int maxY = min (_lineBuffer->maxY, _ifd->maxY);
843
            
844
0
            for (int i = _lineBuffer->minY - _ifd->minY;
845
0
            i <= maxY - _ifd->minY;
846
0
            ++i)
847
0
            {
848
0
                uncompressedSize += (int) _ifd->bytesPerLine[i];
849
0
            }
850
            
851
0
            if (_lineBuffer->compressor &&
852
0
                _lineBuffer->dataSize < uncompressedSize)
853
0
            {
854
0
                _lineBuffer->format = _lineBuffer->compressor->format();
855
                
856
0
                _lineBuffer->dataSize =
857
0
                _lineBuffer->compressor->uncompress (_lineBuffer->buffer,
858
0
                                                     _lineBuffer->dataSize,
859
0
                                                     _lineBuffer->minY,
860
0
                                                     _lineBuffer->uncompressedData);
861
0
            }
862
0
            else
863
0
            {
864
                //
865
                // If the line is uncompressed, it's in XDR format,
866
                // regardless of the compressor's output format.
867
                //
868
                
869
0
                _lineBuffer->format = Compressor::XDR;
870
0
                _lineBuffer->uncompressedData = _lineBuffer->buffer;
871
0
            }
872
0
        }
873
        
874
0
        int yStart, yStop, dy;
875
        
876
0
        if (_ifd->lineOrder == INCREASING_Y)
877
0
        {
878
0
            yStart = _scanLineMin;
879
0
            yStop = _scanLineMax + 1;
880
0
            dy = 1;
881
0
        }
882
0
        else
883
0
        {
884
0
            yStart = _scanLineMax;
885
0
            yStop = _scanLineMin - 1;
886
0
            dy = -1;
887
0
        }
888
        
889
0
        for (int y = yStart; y != yStop; y += dy)
890
0
        {
891
0
            if (modp (y, _optimizationMode._ySampling) != 0)
892
0
                continue;
893
            
894
            //
895
            // Convert one scan line's worth of pixel data back
896
            // from the machine-independent representation, and
897
            // store the result in the frame buffer.
898
            //
899
            
900
            // Set the readPtr to read at the start of uncompressedData
901
            // but with an offet based on calculated array.
902
            // _ifd->offsetInLineBuffer contains offsets based on which
903
            // line we are currently processing.
904
            // Stride will be taken into consideration later.
905
                
906
                
907
0
            const char* readPtr = _lineBuffer->uncompressedData +
908
0
            _ifd->offsetInLineBuffer[y - _ifd->minY];
909
            
910
0
            size_t pixelsToCopySSE = 0;
911
0
            size_t pixelsToCopyNormal = 0;
912
            
913
0
            unsigned short* writePtrLeft = 0;
914
0
            unsigned short* writePtrRight = 0;
915
            
916
0
            size_t channels = _ifd->optimizationData.size();
917
       
918
0
            if(channels>4)
919
0
            {
920
0
                getWritePointerStereo<half>(y, writePtrRight, writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
921
0
            }
922
0
            else 
923
0
            {
924
0
                getWritePointer<half>(y, writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
925
0
            }
926
            
927
0
            if (writePtrRight == 0 && pixelsToCopySSE == 0 && pixelsToCopyNormal == 0)
928
0
            {
929
0
                continue;
930
0
            }
931
            
932
            
933
            //
934
            // support reading up to eight channels
935
            //
936
0
            unsigned short* readPointers[8];
937
            
938
0
            for (size_t i = 0; i < channels ; ++i)
939
0
            {
940
0
                readPointers[i] = (unsigned short*)readPtr + (_ifd->optimizationData[i].offset * (pixelsToCopySSE * 8 + pixelsToCopyNormal));
941
0
            }
942
            
943
            //RGB only
944
0
            if(channels==3 || channels == 6 )
945
0
            {
946
0
                    optimizedWriteToRGB(readPointers[0], readPointers[1], readPointers[2], writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
947
                  
948
                    //stereo RGB
949
0
                    if( channels == 6)
950
0
                    {
951
0
                        optimizedWriteToRGB(readPointers[3], readPointers[4], readPointers[5], writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
952
0
                    }                
953
            //RGBA
954
0
            }else if(channels==4 || channels==8)
955
0
            {
956
                
957
0
                if(_ifd->optimizationData[3].fill)
958
0
                {
959
0
                    optimizedWriteToRGBAFillA(readPointers[0], readPointers[1], readPointers[2], _ifd->optimizationData[3].fillValue.bits() , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
960
0
                }else{
961
0
                    optimizedWriteToRGBA(readPointers[0], readPointers[1], readPointers[2], readPointers[3] , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
962
0
                }
963
                
964
                //stereo RGBA
965
0
                if( channels == 8)
966
0
                {
967
0
                    if(_ifd->optimizationData[7].fill)
968
0
                    {
969
0
                        optimizedWriteToRGBAFillA(readPointers[4], readPointers[5], readPointers[6], _ifd->optimizationData[7].fillValue.bits() , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
970
0
                    }else{
971
0
                        optimizedWriteToRGBA(readPointers[4], readPointers[5], readPointers[6], readPointers[7] , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
972
0
                    }
973
0
                }
974
0
            }
975
0
            else {
976
0
                throw(IEX_NAMESPACE::LogicExc("IIF mode called with incorrect channel pattern"));
977
0
            }
978
            
979
            // If we are in NO_OPTIMIZATION mode, this class will never
980
            // get instantiated, so no need to check for it and duplicate
981
            // the code.
982
0
        }
983
0
    }
984
0
    catch (std::exception &e)
985
0
    {
986
0
        if (!_lineBuffer->hasException)
987
0
        {
988
0
            _lineBuffer->exception = e.what();
989
0
            _lineBuffer->hasException = true;
990
0
        }
991
0
    }
992
0
    catch (...)
993
0
    {
994
0
        if (!_lineBuffer->hasException)
995
0
        {
996
0
            _lineBuffer->exception = "unrecognized exception";
997
0
            _lineBuffer->hasException = true;
998
0
        }
999
0
    }
1000
0
}
1001
#endif
1002
1003
1004
Task *
1005
newLineBufferTask (TaskGroup *group,
1006
                   InputStreamMutex *streamData,
1007
                   ScanLineInputFile::Data *ifd,
1008
                   int number,
1009
                   int scanLineMin,
1010
                   int scanLineMax,
1011
                   OptimizationMode optimizationMode)
1012
0
{
1013
     //
1014
     // Wait for a line buffer to become available, fill the line
1015
     // buffer with raw data from the file if necessary, and create
1016
     // a new LineBufferTask whose execute() method will uncompress
1017
     // the contents of the buffer and copy the pixels into the
1018
     // frame buffer.
1019
     //
1020
     
1021
0
     LineBuffer *lineBuffer = ifd->getLineBuffer (number);
1022
     
1023
0
     try
1024
0
     {
1025
0
         lineBuffer->wait ();
1026
         
1027
0
         if (lineBuffer->number != number)
1028
0
         {
1029
0
             lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer;
1030
0
             lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1;
1031
             
1032
0
             lineBuffer->number = number;
1033
0
             lineBuffer->uncompressedData = 0;
1034
             
1035
0
             readPixelData (streamData, ifd, lineBuffer->minY,
1036
0
                            lineBuffer->buffer,
1037
0
                            lineBuffer->dataSize);
1038
0
         }
1039
0
     }
1040
0
     catch (std::exception &e)
1041
0
     {
1042
0
         if (!lineBuffer->hasException)
1043
0
         {
1044
0
             lineBuffer->exception = e.what();
1045
0
             lineBuffer->hasException = true;
1046
0
         }
1047
0
         lineBuffer->number = -1;
1048
0
         lineBuffer->post();
1049
0
         throw;
1050
0
     }
1051
0
     catch (...)
1052
0
     {
1053
         //
1054
         // Reading from the file caused an exception.
1055
         // Signal that the line buffer is free, and
1056
         // re-throw the exception.
1057
         //
1058
         
1059
0
         lineBuffer->exception = "unrecognized exception";
1060
0
         lineBuffer->hasException = true;
1061
0
         lineBuffer->number = -1;
1062
0
         lineBuffer->post();
1063
0
         throw;
1064
0
     }
1065
     
1066
0
     scanLineMin = max (lineBuffer->minY, scanLineMin);
1067
0
     scanLineMax = min (lineBuffer->maxY, scanLineMax);
1068
     
1069
     
1070
0
     Task* retTask = 0;
1071
     
1072
0
#ifdef IMF_HAVE_SSE2     
1073
0
     if (optimizationMode._optimizable)
1074
0
     {
1075
         
1076
0
         retTask = new LineBufferTaskIIF (group, ifd, lineBuffer,
1077
0
                                          scanLineMin, scanLineMax,
1078
0
                                          optimizationMode);
1079
      
1080
0
     }
1081
0
     else
1082
0
#endif         
1083
0
     {
1084
0
         retTask = new LineBufferTask (group, ifd, lineBuffer,
1085
0
                                       scanLineMin, scanLineMax,
1086
0
                                       optimizationMode);
1087
0
     }
1088
     
1089
0
     return retTask;
1090
     
1091
0
 }
1092
 
1093
  
1094
1095
1096
} // namespace
1097
1098
1099
void ScanLineInputFile::initialize(const Header& header)
1100
0
{
1101
0
    try
1102
0
    {
1103
0
        _data->header = header;
1104
1105
0
        _data->lineOrder = _data->header.lineOrder();
1106
1107
0
        const Box2i &dataWindow = _data->header.dataWindow();
1108
1109
0
        _data->minX = dataWindow.min.x;
1110
0
        _data->maxX = dataWindow.max.x;
1111
0
        _data->minY = dataWindow.min.y;
1112
0
        _data->maxY = dataWindow.max.y;
1113
1114
0
        size_t maxBytesPerLine = bytesPerLineTable (_data->header,
1115
0
                                                    _data->bytesPerLine);
1116
1117
0
        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1118
0
        {
1119
0
            _data->lineBuffers[i] = new LineBuffer (newCompressor
1120
0
                                                (_data->header.compression(),
1121
0
                                                 maxBytesPerLine,
1122
0
                                                 _data->header));
1123
0
        }
1124
1125
0
        _data->linesInBuffer =
1126
0
            numLinesInBuffer (_data->lineBuffers[0]->compressor);
1127
1128
0
        _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
1129
1130
0
        if (!_streamData->is->isMemoryMapped())
1131
0
        {
1132
0
            for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1133
0
            {
1134
0
                _data->lineBuffers[i]->buffer = (char *) EXRAllocAligned(_data->lineBufferSize*sizeof(char),16);
1135
0
            }
1136
0
        }
1137
0
        _data->nextLineBufferMinY = _data->minY - 1;
1138
1139
0
        offsetInLineBufferTable (_data->bytesPerLine,
1140
0
                                 _data->linesInBuffer,
1141
0
                                 _data->offsetInLineBuffer);
1142
1143
0
        int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
1144
0
                              _data->linesInBuffer) / _data->linesInBuffer;
1145
1146
0
        _data->lineOffsets.resize (lineOffsetSize);
1147
0
    }
1148
0
    catch (...)
1149
0
    {
1150
0
        delete _data;
1151
0
        _data=NULL;
1152
0
        throw;
1153
0
    }
1154
0
}
1155
1156
1157
ScanLineInputFile::ScanLineInputFile(InputPartData* part)
1158
0
{
1159
0
    if (part->header.type() != SCANLINEIMAGE)
1160
0
        throw IEX_NAMESPACE::ArgExc("Can't build a ScanLineInputFile from a type-mismatched part.");
1161
1162
0
    _data = new Data(part->numThreads);
1163
0
    _streamData = part->mutex;
1164
0
    _data->memoryMapped = _streamData->is->isMemoryMapped();
1165
1166
0
    _data->version = part->version;
1167
1168
0
    initialize(part->header);
1169
1170
0
    _data->lineOffsets = part->chunkOffsets;
1171
1172
0
    _data->partNumber = part->partNumber;
1173
    //
1174
    // (TODO) change this code later.
1175
    // The completeness of the file should be detected in MultiPartInputFile.
1176
    //
1177
0
    _data->fileIsComplete = true;
1178
0
}
1179
1180
1181
ScanLineInputFile::ScanLineInputFile
1182
    (const Header &header,
1183
     OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
1184
     int numThreads)
1185
:
1186
0
    _data (new Data (numThreads)),
1187
0
    _streamData (new InputStreamMutex())
1188
0
{
1189
0
    _streamData->is = is;
1190
0
    _data->memoryMapped = is->isMemoryMapped();
1191
1192
0
    initialize(header);
1193
    
1194
    //
1195
    // (TODO) this is nasty - we need a better way of working out what type of file has been used.
1196
    // in any case I believe this constructor only gets used with single part files
1197
    // and 'version' currently only tracks multipart state, so setting to 0 (not multipart) works for us
1198
    //
1199
    
1200
0
    _data->version=0;
1201
0
    readLineOffsets (*_streamData->is,
1202
0
                     _data->lineOrder,
1203
0
                     _data->lineOffsets,
1204
0
                     _data->fileIsComplete);
1205
0
}
1206
1207
1208
ScanLineInputFile::~ScanLineInputFile ()
1209
0
{
1210
0
    if (!_data->memoryMapped)
1211
0
    {
1212
0
        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1213
0
        {
1214
0
            EXRFreeAligned(_data->lineBuffers[i]->buffer);
1215
0
        }
1216
0
    }
1217
            
1218
1219
    //
1220
    // ScanLineInputFile should never delete the stream,
1221
    // because it does not own the stream.
1222
    // We just delete the Mutex here.
1223
    //
1224
0
    if (_data->partNumber == -1)
1225
0
        delete _streamData;
1226
1227
0
    delete _data;
1228
0
}
1229
1230
1231
const char *
1232
ScanLineInputFile::fileName () const
1233
0
{
1234
0
    return _streamData->is->fileName();
1235
0
}
1236
1237
1238
const Header &
1239
ScanLineInputFile::header () const
1240
0
{
1241
0
    return _data->header;
1242
0
}
1243
1244
1245
int
1246
ScanLineInputFile::version () const
1247
0
{
1248
0
    return _data->version;
1249
0
}
1250
1251
1252
namespace
1253
{
1254
    
1255
    
1256
// returns the optimization state for the given arrangement of frame bufers
1257
// this assumes:
1258
//   both the file and framebuffer are half float data
1259
//   both the file and framebuffer have xSampling and ySampling=1
1260
//   entries in optData are sorted into their interleave order (i.e. by base address)
1261
//   These tests are done by SetFrameBuffer as it is building optData
1262
//  
1263
OptimizationMode
1264
detectOptimizationMode (const vector<sliceOptimizationData>& optData)
1265
0
{
1266
0
    OptimizationMode w;
1267
    
1268
    // need to be compiled with SSE optimisations: if not, just returns false
1269
0
#ifdef IMF_HAVE_SSE2
1270
    
1271
    
1272
    // only handle reading 3,4,6 or 8 channels
1273
0
    switch(optData.size())
1274
0
    {
1275
0
        case 3 : break;
1276
0
        case 4 : break;
1277
0
        case 6 : break;
1278
0
        case 8 : break;
1279
0
        default :
1280
0
            return w;
1281
0
    }
1282
    
1283
    //
1284
    // the point at which data switches between the primary and secondary bank
1285
    //
1286
0
    size_t bankSize = optData.size()>4 ? optData.size()/2 : optData.size();
1287
    
1288
0
    for(size_t i=0;i<optData.size();i++)
1289
0
    {
1290
0
        const sliceOptimizationData& data = optData[i];
1291
        // can't fill anything other than channel 3 or channel 7
1292
0
        if(data.fill)
1293
0
        {
1294
0
            if(i!=3 && i!=7)
1295
0
            {
1296
0
                return w;
1297
0
            }
1298
0
        }
1299
        
1300
        // cannot have gaps in the channel layout, so the stride must be (number of channels written in the bank)*2
1301
0
        if(data.xStride !=bankSize*2)
1302
0
        {
1303
0
            return w;
1304
0
        }
1305
        
1306
        // each bank of channels must be channel interleaved: each channel base pointer must be (previous channel+2)
1307
        // this also means channel sampling pattern must be consistent, as must yStride
1308
0
        if(i!=0 && i!=bankSize)
1309
0
        {
1310
0
            if(data.base!=optData[i-1].base+2)
1311
0
            {
1312
0
                return w;
1313
0
            }
1314
0
        }
1315
0
        if(i!=0)
1316
0
        {
1317
            
1318
0
            if(data.yStride!=optData[i-1].yStride)
1319
0
            {
1320
0
                return w;
1321
0
            }
1322
0
        }
1323
0
    }
1324
    
1325
1326
0
    w._ySampling=optData[0].ySampling;
1327
0
    w._optimizable=true;
1328
    
1329
0
#endif
1330
1331
0
    return w;
1332
0
}
1333
1334
1335
} // Anonymous namespace
1336
1337
void  
1338
ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1339
0
{
1340
0
    Lock lock (*_streamData);
1341
1342
    
1343
    
1344
0
    const ChannelList &channels = _data->header.channels();
1345
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1346
0
   j != frameBuffer.end();
1347
0
   ++j)
1348
0
    {
1349
0
  ChannelList::ConstIterator i = channels.find (j.name());
1350
1351
0
  if (i == channels.end())
1352
0
      continue;
1353
1354
0
  if (i.channel().xSampling != j.slice().xSampling ||
1355
0
      i.channel().ySampling != j.slice().ySampling)
1356
0
      THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1357
0
        "of \"" << i.name() << "\" channel "
1358
0
        "of input file \"" << fileName() << "\" are "
1359
0
        "not compatible with the frame buffer's "
1360
0
        "subsampling factors.");
1361
0
    }
1362
1363
    // optimization is possible if this is a little endian system
1364
    // and both inputs and outputs are half floats
1365
    // 
1366
0
    bool optimizationPossible = true;
1367
    
1368
0
    if (!GLOBAL_SYSTEM_LITTLE_ENDIAN)
1369
0
    {
1370
0
        optimizationPossible =false;
1371
0
    }
1372
    
1373
0
    vector<sliceOptimizationData> optData;
1374
    
1375
1376
    //
1377
    // Initialize the slice table for readPixels().
1378
    //
1379
1380
0
    vector<InSliceInfo> slices;
1381
0
    ChannelList::ConstIterator i = channels.begin();
1382
    
1383
    // current offset of channel: pixel data starts at offset*width into the
1384
    // decompressed scanline buffer
1385
0
    size_t offset = 0;
1386
    
1387
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1388
0
   j != frameBuffer.end();
1389
0
   ++j)
1390
0
    {
1391
0
  while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1392
0
  {
1393
      //
1394
      // Channel i is present in the file but not
1395
      // in the frame buffer; data for channel i
1396
      // will be skipped during readPixels().
1397
      //
1398
1399
0
      slices.push_back (InSliceInfo (i.channel().type,
1400
0
             i.channel().type,
1401
0
             0, // base
1402
0
             0, // xStride
1403
0
             0, // yStride
1404
0
             i.channel().xSampling,
1405
0
             i.channel().ySampling,
1406
0
             false,  // fill
1407
0
             true, // skip
1408
0
             0.0)); // fillValue
1409
      
1410
0
              switch(i.channel().type)
1411
0
              {
1412
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1413
0
                      offset++;
1414
0
                      break;
1415
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1416
0
                      offset+=2;
1417
0
                      break;
1418
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1419
0
                      offset+=2;
1420
0
                      break;
1421
0
              }
1422
0
              ++i;
1423
0
  }
1424
1425
0
  bool fill = false;
1426
1427
0
  if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1428
0
  {
1429
      //
1430
      // Channel i is present in the frame buffer, but not in the file.
1431
      // In the frame buffer, slice j will be filled with a default value.
1432
      //
1433
1434
0
      fill = true;
1435
0
  }
1436
1437
0
  slices.push_back (InSliceInfo (j.slice().type,
1438
0
               fill? j.slice().type:
1439
0
                     i.channel().type,
1440
0
               j.slice().base,
1441
0
               j.slice().xStride,
1442
0
               j.slice().yStride,
1443
0
               j.slice().xSampling,
1444
0
               j.slice().ySampling,
1445
0
               fill,
1446
0
               false, // skip
1447
0
               j.slice().fillValue));
1448
1449
0
          if(!fill && i.channel().type!=OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1450
0
          {
1451
0
              optimizationPossible = false;
1452
0
          }
1453
          
1454
0
          if(j.slice().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1455
0
          {
1456
0
              optimizationPossible = false;
1457
0
          }
1458
0
          if(j.slice().xSampling!=1 || j.slice().ySampling!=1)
1459
0
          {
1460
0
              optimizationPossible = false;
1461
0
          }
1462
1463
          
1464
0
          if(optimizationPossible)
1465
0
          {
1466
0
              sliceOptimizationData dat;
1467
0
              dat.base = j.slice().base;
1468
0
              dat.fill = fill;
1469
0
              dat.fillValue = j.slice().fillValue;
1470
0
              dat.offset = offset;
1471
0
              dat.xStride = j.slice().xStride;
1472
0
              dat.yStride = j.slice().yStride;
1473
0
              dat.xSampling = j.slice().xSampling;
1474
0
              dat.ySampling = j.slice().ySampling;
1475
0
              optData.push_back(dat);
1476
0
          }
1477
          
1478
0
          if(!fill)
1479
0
          {
1480
0
              switch(i.channel().type)
1481
0
              {
1482
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1483
0
                      offset++;
1484
0
                      break;
1485
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1486
0
                      offset+=2;
1487
0
                      break;
1488
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1489
0
                      offset+=2;
1490
0
                      break;
1491
0
              }
1492
0
          }
1493
          
1494
1495
          
1496
0
  if (i != channels.end() && !fill)
1497
0
      ++i;
1498
0
    }
1499
1500
   
1501
0
   if(optimizationPossible)
1502
0
   {
1503
       //
1504
       // check optimisibility
1505
       // based on channel ordering and fill channel positions
1506
       //
1507
0
       sort(optData.begin(),optData.end());
1508
0
       _data->optimizationMode = detectOptimizationMode(optData);
1509
0
   }
1510
   
1511
0
   if(!optimizationPossible || _data->optimizationMode._optimizable==false)
1512
0
   {   
1513
0
       optData = vector<sliceOptimizationData>();
1514
0
       _data->optimizationMode._optimizable=false;
1515
0
   }
1516
    
1517
    //
1518
    // Store the new frame buffer.
1519
    //
1520
1521
0
    _data->frameBuffer = frameBuffer;
1522
0
    _data->slices = slices;
1523
0
    _data->optimizationData = optData;
1524
0
}
1525
1526
1527
const FrameBuffer &
1528
ScanLineInputFile::frameBuffer () const
1529
0
{
1530
0
    Lock lock (*_streamData);
1531
0
    return _data->frameBuffer;
1532
0
}
1533
1534
1535
bool
1536
ScanLineInputFile::isComplete () const
1537
0
{
1538
0
    return _data->fileIsComplete;
1539
0
}
1540
1541
bool ScanLineInputFile::isOptimizationEnabled() const
1542
0
{
1543
0
    if (_data->slices.size() == 0)
1544
0
        throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1545
0
        "as pixel data destination.");
1546
    
1547
0
    return _data->optimizationMode._optimizable;
1548
0
}
1549
1550
1551
void  
1552
ScanLineInputFile::readPixels (int scanLine1, int scanLine2)
1553
0
{
1554
0
    try
1555
0
    {
1556
0
        Lock lock (*_streamData);
1557
1558
0
  if (_data->slices.size() == 0)
1559
0
      throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1560
0
             "as pixel data destination.");
1561
1562
0
  int scanLineMin = min (scanLine1, scanLine2);
1563
0
  int scanLineMax = max (scanLine1, scanLine2);
1564
1565
0
  if (scanLineMin < _data->minY || scanLineMax > _data->maxY)
1566
0
      throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1567
0
             "the image file's data window.");
1568
1569
        //
1570
        // We impose a numbering scheme on the lineBuffers where the first
1571
        // scanline is contained in lineBuffer 1.
1572
        //
1573
        // Determine the first and last lineBuffer numbers in this scanline
1574
        // range. We always attempt to read the scanlines in the order that
1575
        // they are stored in the file.
1576
        //
1577
1578
0
        int start, stop, dl;
1579
1580
0
        if (_data->lineOrder == INCREASING_Y)
1581
0
        {
1582
0
            start = (scanLineMin - _data->minY) / _data->linesInBuffer;
1583
0
            stop  = (scanLineMax - _data->minY) / _data->linesInBuffer + 1;
1584
0
            dl = 1;
1585
0
        }
1586
0
        else
1587
0
        {
1588
0
            start = (scanLineMax - _data->minY) / _data->linesInBuffer;
1589
0
            stop  = (scanLineMin - _data->minY) / _data->linesInBuffer - 1;
1590
0
            dl = -1;
1591
0
        }
1592
1593
        //
1594
        // Create a task group for all line buffer tasks.  When the
1595
  // task group goes out of scope, the destructor waits until
1596
  // all tasks are complete.
1597
        //
1598
        
1599
0
        {
1600
0
            TaskGroup taskGroup;
1601
    
1602
            //
1603
            // Add the line buffer tasks.
1604
            //
1605
            // The tasks will execute in the order that they are created
1606
            // because we lock the line buffers during construction and the
1607
            // constructors are called by the main thread.  Hence, in order
1608
      // for a successive task to execute the previous task which
1609
      // used that line buffer must have completed already.
1610
            //
1611
    
1612
0
            for (int l = start; l != stop; l += dl)
1613
0
            {
1614
0
                ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup,
1615
0
                                                              _streamData,
1616
0
                                                              _data, l,
1617
0
                                                              scanLineMin,
1618
0
                                                              scanLineMax,
1619
0
                                                              _data->optimizationMode));
1620
0
            }
1621
        
1622
      //
1623
            // finish all tasks
1624
      //
1625
0
        }
1626
        
1627
  //
1628
  // Exeption handling:
1629
  //
1630
  // LineBufferTask::execute() may have encountered exceptions, but
1631
  // those exceptions occurred in another thread, not in the thread
1632
  // that is executing this call to ScanLineInputFile::readPixels().
1633
  // LineBufferTask::execute() has caught all exceptions and stored
1634
  // the exceptions' what() strings in the line buffers.
1635
  // Now we check if any line buffer contains a stored exception; if
1636
  // this is the case then we re-throw the exception in this thread.
1637
  // (It is possible that multiple line buffers contain stored
1638
  // exceptions.  We re-throw the first exception we find and
1639
  // ignore all others.)
1640
  //
1641
1642
0
  const string *exception = 0;
1643
1644
0
        for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
1645
0
  {
1646
0
            LineBuffer *lineBuffer = _data->lineBuffers[i];
1647
1648
0
      if (lineBuffer->hasException && !exception)
1649
0
    exception = &lineBuffer->exception;
1650
1651
0
      lineBuffer->hasException = false;
1652
0
  }
1653
1654
0
  if (exception)
1655
0
      throw IEX_NAMESPACE::IoExc (*exception);
1656
0
    }
1657
0
    catch (IEX_NAMESPACE::BaseExc &e)
1658
0
    {
1659
0
  REPLACE_EXC (e, "Error reading pixel data from image "
1660
0
                 "file \"" << fileName() << "\". " << e.what());
1661
0
  throw;
1662
0
    }
1663
0
}
1664
1665
1666
void  
1667
ScanLineInputFile::readPixels (int scanLine)
1668
0
{
1669
0
    readPixels (scanLine, scanLine);
1670
0
}
1671
1672
1673
void
1674
ScanLineInputFile::rawPixelData (int firstScanLine,
1675
         const char *&pixelData,
1676
         int &pixelDataSize)
1677
0
{
1678
0
    try
1679
0
    {
1680
0
        Lock lock (*_streamData);
1681
1682
0
  if (firstScanLine < _data->minY || firstScanLine > _data->maxY)
1683
0
  {
1684
0
      throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1685
0
             "the image file's data window.");
1686
0
  }
1687
1688
0
        int minY = lineBufferMinY
1689
0
      (firstScanLine, _data->minY, _data->linesInBuffer);
1690
1691
0
  readPixelData
1692
0
      (_streamData, _data, minY, _data->lineBuffers[0]->buffer, pixelDataSize);
1693
1694
0
  pixelData = _data->lineBuffers[0]->buffer;
1695
0
    }
1696
0
    catch (IEX_NAMESPACE::BaseExc &e)
1697
0
    {
1698
0
  REPLACE_EXC (e, "Error reading pixel data from image "
1699
0
                 "file \"" << fileName() << "\". " << e.what());
1700
0
  throw;
1701
0
    }
1702
0
}
1703
1704
1705
void ScanLineInputFile::rawPixelDataToBuffer(int scanLine,
1706
                                             char *pixelData,
1707
                                             int &pixelDataSize) const
1708
0
{
1709
0
  if (_data->memoryMapped) {
1710
0
    throw IEX_NAMESPACE::ArgExc ("Reading raw pixel data to a buffer "
1711
0
                                 "is not supported for memory mapped "
1712
0
                                 "streams." );
1713
0
  }
1714
1715
0
  try 
1716
0
  {
1717
0
    Lock lock (*_streamData);
1718
    
1719
0
    if (scanLine < _data->minY || scanLine > _data->maxY) 
1720
0
    {
1721
0
      throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1722
0
                                   "the image file's data window.");
1723
0
    }
1724
    
1725
0
    readPixelData
1726
0
      (_streamData, _data, scanLine, pixelData, pixelDataSize);
1727
    
1728
0
  }
1729
0
  catch (IEX_NAMESPACE::BaseExc &e) 
1730
0
  {
1731
0
    REPLACE_EXC (e, "Error reading pixel data from image "
1732
0
                 "file \"" << fileName() << "\". " << e.what());
1733
0
    throw;
1734
0
  }
1735
0
}
1736
1737
1738
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT