Coverage Report

Created: 2023-12-08 06:53

/src/freeimage-svn/FreeImage/trunk/Source/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
    typeInFrameBuffer (tifb),
124
    typeInFile (tifl),
125
    base (b),
126
    xStride (xs),
127
    yStride (ys),
128
    xSampling (xsm),
129
    ySampling (ysm),
130
    fill (f),
131
    skip (s),
132
    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
    uncompressedData (0),
165
    buffer (0),
166
    dataSize (0),
167
    compressor (comp),
168
    format (defaultFormat(compressor)),
169
    number (-1),
170
    hasException (false),
171
    exception (),
172
    _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
        partNumber(-1),
253
        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
379
0
    Int64 lineOffset = ifd->lineOffsets[lineBufferNumber];
380
381
0
    if (lineOffset == 0)
382
0
  THROW (IEX_NAMESPACE::InputExc, "Scan line " << minY << " is missing.");
383
384
    //
385
    // Seek to the start of the scan line in the file,
386
    // if necessary.
387
    //
388
389
0
    if ( !isMultiPart(ifd->version) )
390
0
    {
391
0
        if (ifd->nextLineBufferMinY != minY)
392
0
            streamData->is->seekg (lineOffset);
393
0
    }
394
0
    else
395
0
    {
396
        //
397
        // In a multi-part file, the file pointer may have been moved by
398
        // other parts, so we have to ask tellg() where we are.
399
        //
400
0
        if (streamData->is->tellg() != ifd->lineOffsets[lineBufferNumber])
401
0
            streamData->is->seekg (lineOffset);
402
0
    }
403
404
    //
405
    // Read the data block's header.
406
    //
407
408
0
    int yInFile;
409
410
    //
411
    // Read the part number when we are dealing with a multi-part file.
412
    //
413
0
    if (isMultiPart(ifd->version))
414
0
    {
415
0
        int partNumber;
416
0
        OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, partNumber);
417
0
        if (partNumber != ifd->partNumber)
418
0
        {
419
0
            THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
420
0
                   << ", should be " << ifd->partNumber << ".");
421
0
        }
422
0
    }
423
424
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, yInFile);
425
0
    OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
426
    
427
0
    if (yInFile != minY)
428
0
        throw IEX_NAMESPACE::InputExc ("Unexpected data block y coordinate.");
429
430
0
    if (dataSize > (int) ifd->lineBufferSize)
431
0
  throw IEX_NAMESPACE::InputExc ("Unexpected data block length.");
432
433
    //
434
    // Read the pixel data.
435
    //
436
437
0
    if (streamData->is->isMemoryMapped ())
438
0
        buffer = streamData->is->readMemoryMapped (dataSize);
439
0
    else
440
0
        streamData->is->read (buffer, dataSize);
441
442
    //
443
    // Keep track of which scan line is the next one in
444
    // the file, so that we can avoid redundant seekg()
445
    // operations (seekg() can be fairly expensive).
446
    //
447
448
0
    if (ifd->lineOrder == INCREASING_Y)
449
0
        ifd->nextLineBufferMinY = minY + ifd->linesInBuffer;
450
0
    else
451
0
        ifd->nextLineBufferMinY = minY - ifd->linesInBuffer;
452
0
}
453
454
                        
455
456
//
457
// A LineBufferTask encapsulates the task uncompressing a set of
458
// scanlines (line buffer) and copying them into the frame buffer.
459
//
460
461
class LineBufferTask : public Task
462
{
463
  public:
464
465
    LineBufferTask (TaskGroup *group,
466
                    ScanLineInputFile::Data *ifd,
467
        LineBuffer *lineBuffer,
468
                    int scanLineMin,
469
        int scanLineMax,
470
                    OptimizationMode optimizationMode);
471
472
    virtual ~LineBufferTask ();
473
474
    virtual void    execute ();
475
476
  private:
477
478
    ScanLineInputFile::Data * _ifd;
479
    LineBuffer *    _lineBuffer;
480
    int       _scanLineMin;
481
    int       _scanLineMax;
482
    OptimizationMode            _optimizationMode;
483
};
484
485
486
LineBufferTask::LineBufferTask
487
    (TaskGroup *group,
488
     ScanLineInputFile::Data *ifd,
489
     LineBuffer *lineBuffer,
490
     int scanLineMin,
491
     int scanLineMax,OptimizationMode optimizationMode)
492
:
493
    Task (group),
494
    _ifd (ifd),
495
    _lineBuffer (lineBuffer),
496
    _scanLineMin (scanLineMin),
497
    _scanLineMax (scanLineMax),
498
    _optimizationMode(optimizationMode)
499
0
{
500
    // empty
501
0
}
502
503
504
LineBufferTask::~LineBufferTask ()
505
0
{
506
    //
507
    // Signal that the line buffer is now free
508
    //
509
510
0
    _lineBuffer->post ();
511
0
}
512
513
514
void
515
LineBufferTask::execute ()
516
0
{
517
0
    try
518
0
    {
519
        //
520
        // Uncompress the data, if necessary
521
        //
522
    
523
0
        if (_lineBuffer->uncompressedData == 0)
524
0
        {
525
0
            int uncompressedSize = 0;
526
0
            int maxY = min (_lineBuffer->maxY, _ifd->maxY);
527
    
528
0
            for (int i = _lineBuffer->minY - _ifd->minY;
529
0
                 i <= maxY - _ifd->minY;
530
0
     ++i)
531
0
      {
532
0
                uncompressedSize += (int) _ifd->bytesPerLine[i];
533
0
      }
534
    
535
0
            if (_lineBuffer->compressor &&
536
0
                _lineBuffer->dataSize < uncompressedSize)
537
0
            {
538
0
                _lineBuffer->format = _lineBuffer->compressor->format();
539
540
0
                _lineBuffer->dataSize = _lineBuffer->compressor->uncompress
541
0
                    (_lineBuffer->buffer,
542
0
                     _lineBuffer->dataSize,
543
0
         _lineBuffer->minY,
544
0
                     _lineBuffer->uncompressedData);
545
0
            }
546
0
            else
547
0
            {
548
                //
549
                // If the line is uncompressed, it's in XDR format,
550
                // regardless of the compressor's output format.
551
                //
552
    
553
0
                _lineBuffer->format = Compressor::XDR;
554
0
                _lineBuffer->uncompressedData = _lineBuffer->buffer;
555
0
            }
556
0
        }
557
        
558
0
        int yStart, yStop, dy;
559
560
0
        if (_ifd->lineOrder == INCREASING_Y)
561
0
        {
562
0
            yStart = _scanLineMin;
563
0
            yStop = _scanLineMax + 1;
564
0
            dy = 1;
565
0
        }
566
0
        else
567
0
        {
568
0
            yStart = _scanLineMax;
569
0
            yStop = _scanLineMin - 1;
570
0
            dy = -1;
571
0
        }
572
    
573
0
        for (int y = yStart; y != yStop; y += dy)
574
0
        {
575
            //
576
            // Convert one scan line's worth of pixel data back
577
            // from the machine-independent representation, and
578
            // store the result in the frame buffer.
579
            //
580
    
581
0
            const char *readPtr = _lineBuffer->uncompressedData +
582
0
                                  _ifd->offsetInLineBuffer[y - _ifd->minY];
583
    
584
            //
585
            // Iterate over all image channels.
586
            //
587
    
588
0
            for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
589
0
            {
590
                //
591
                // Test if scan line y of this channel contains any data
592
    // (the scan line contains data only if y % ySampling == 0).
593
                //
594
    
595
0
                const InSliceInfo &slice = _ifd->slices[i];
596
    
597
0
                if (modp (y, slice.ySampling) != 0)
598
0
                    continue;
599
    
600
                //
601
                // Find the x coordinates of the leftmost and rightmost
602
                // sampled pixels (i.e. pixels within the data window
603
                // for which x % xSampling == 0).
604
                //
605
    
606
0
                int dMinX = divp (_ifd->minX, slice.xSampling);
607
0
                int dMaxX = divp (_ifd->maxX, slice.xSampling);
608
    
609
                //
610
    // Fill the frame buffer with pixel data.
611
                //
612
    
613
0
                if (slice.skip)
614
0
                {
615
                    //
616
                    // The file contains data for this channel, but
617
                    // the frame buffer contains no slice for this channel.
618
                    //
619
    
620
0
                    skipChannel (readPtr, slice.typeInFile, dMaxX - dMinX + 1);
621
0
                }
622
0
                else
623
0
                {
624
                    //
625
                    // The frame buffer contains a slice for this channel.
626
                    //
627
    
628
0
                    char *linePtr  = slice.base +
629
0
                                        divp (y, slice.ySampling) *
630
0
                                        slice.yStride;
631
    
632
0
                    char *writePtr = linePtr + dMinX * slice.xStride;
633
0
                    char *endPtr   = linePtr + dMaxX * slice.xStride;
634
                    
635
0
                    copyIntoFrameBuffer (readPtr, writePtr, endPtr,
636
0
                                         slice.xStride, slice.fill,
637
0
                                         slice.fillValue, _lineBuffer->format,
638
0
                                         slice.typeInFrameBuffer,
639
0
                                         slice.typeInFile);
640
0
                }
641
0
            }
642
0
        }
643
0
    }
644
0
    catch (std::exception &e)
645
0
    {
646
0
        if (!_lineBuffer->hasException)
647
0
        {
648
0
            _lineBuffer->exception = e.what();
649
0
            _lineBuffer->hasException = true;
650
0
        }
651
0
    }
652
0
    catch (...)
653
0
    {
654
0
        if (!_lineBuffer->hasException)
655
0
        {
656
0
            _lineBuffer->exception = "unrecognized exception";
657
0
            _lineBuffer->hasException = true;
658
0
        }
659
0
    }
660
0
}
661
662
663
#ifdef IMF_HAVE_SSE2
664
//
665
// IIF format is more restricted than a perfectly generic one,
666
// so it is possible to perform some optimizations.
667
//
668
class LineBufferTaskIIF : public Task
669
{
670
    public:
671
        
672
        LineBufferTaskIIF (TaskGroup *group,
673
                           ScanLineInputFile::Data *ifd,
674
                           LineBuffer *lineBuffer,
675
                           int scanLineMin,
676
                           int scanLineMax,
677
                           OptimizationMode optimizationMode);
678
                           
679
        virtual ~LineBufferTaskIIF ();
680
                           
681
        virtual void                execute ();
682
        
683
        template<typename TYPE>
684
        void getWritePointer (int y,
685
                              unsigned short*& pOutWritePointerRight,
686
                              size_t& outPixelsToCopySSE,
687
                              size_t& outPixelsToCopyNormal,int bank=0) const;
688
                              
689
        template<typename TYPE>
690
        void getWritePointerStereo (int y,
691
                                    unsigned short*& outWritePointerRight,
692
                                    unsigned short*& outWritePointerLeft,
693
                                    size_t& outPixelsToCopySSE,
694
                                    size_t& outPixelsToCopyNormal) const;
695
696
    private:
697
        
698
        ScanLineInputFile::Data *   _ifd;
699
        LineBuffer *                _lineBuffer;
700
        int                         _scanLineMin;
701
        int                         _scanLineMax;
702
        OptimizationMode            _optimizationMode;
703
  
704
};
705
706
LineBufferTaskIIF::LineBufferTaskIIF
707
    (TaskGroup *group,
708
     ScanLineInputFile::Data *ifd,
709
     LineBuffer *lineBuffer,
710
     int scanLineMin,
711
     int scanLineMax,
712
     OptimizationMode optimizationMode
713
    )
714
    :
715
     Task (group),
716
     _ifd (ifd),
717
     _lineBuffer (lineBuffer),
718
     _scanLineMin (scanLineMin),
719
     _scanLineMax (scanLineMax),
720
     _optimizationMode (optimizationMode)
721
0
{
722
     /*
723
     //
724
     // indicates the optimised path has been taken
725
     //
726
     static bool could_optimise=false;
727
     if(could_optimise==false)
728
     {
729
         std::cerr << " optimised path\n";
730
         could_optimise=true;
731
     }
732
     */
733
0
}
734
 
735
LineBufferTaskIIF::~LineBufferTaskIIF ()
736
0
{
737
     //
738
     // Signal that the line buffer is now free
739
     //
740
     
741
0
     _lineBuffer->post ();
742
0
}
743
 
744
// Return 0 if we are to skip because of sampling
745
// channelBank is 0 for the first group of channels, 1 for the second
746
template<typename TYPE>
747
void LineBufferTaskIIF::getWritePointer 
748
                            (int y,
749
                             unsigned short*& outWritePointerRight,
750
                             size_t& outPixelsToCopySSE,
751
                             size_t& outPixelsToCopyNormal,
752
                             int channelBank
753
                            ) const
754
0
{
755
      // Channels are saved alphabetically, so the order is B G R.
756
      // The last slice (R) will give us the location of our write pointer.
757
      // The only slice that we support skipping is alpha, i.e. the first one.  
758
      // This does not impact the write pointer or the pixels to copy at all.
759
      
760
0
      size_t nbSlicesInBank = _ifd->optimizationData.size();
761
      
762
0
      int sizeOfSingleValue = sizeof(TYPE);
763
      
764
0
      if(_ifd->optimizationData.size()>4)
765
0
      {
766
          // there are two banks - we only copy one at once
767
0
          nbSlicesInBank/=2;
768
0
      }
769
770
      
771
0
      size_t firstChannel = 0;
772
0
      if(channelBank==1)
773
0
      {
774
0
          firstChannel = _ifd->optimizationData.size()/2;
775
0
      }
776
      
777
0
       sliceOptimizationData& firstSlice = _ifd->optimizationData[firstChannel];
778
      
779
0
      if (modp (y, firstSlice.ySampling) != 0)
780
0
      {
781
0
          outPixelsToCopySSE    = 0;
782
0
          outPixelsToCopyNormal = 0;
783
0
          outWritePointerRight  = 0;
784
0
      }
785
      
786
0
      const char* linePtr1  = firstSlice.base +
787
0
      divp (y, firstSlice.ySampling) *
788
0
      firstSlice.yStride;
789
      
790
0
      int dMinX1 = divp (_ifd->minX, firstSlice.xSampling);
791
0
      int dMaxX1 = divp (_ifd->maxX, firstSlice.xSampling);
792
      
793
      // Construct the writePtr so that we start writing at
794
      // linePtr + Min offset in the line.
795
0
      outWritePointerRight =  (unsigned short*)(linePtr1 +
796
0
      dMinX1 * firstSlice.xStride );
797
      
798
0
      size_t bytesToCopy  = ((linePtr1 + dMaxX1 * firstSlice.xStride ) -
799
0
      (linePtr1 + dMinX1 * firstSlice.xStride )) + 2;
800
0
      size_t shortsToCopy = bytesToCopy / sizeOfSingleValue;
801
0
      size_t pixelsToCopy = (shortsToCopy / nbSlicesInBank ) + 1;
802
      
803
      // We only support writing to SSE if we have no pixels to copy normally
804
0
      outPixelsToCopySSE    = pixelsToCopy / 8;
805
0
      outPixelsToCopyNormal = pixelsToCopy % 8;
806
      
807
0
}
808
809
810
template<typename TYPE>
811
void LineBufferTaskIIF::getWritePointerStereo 
812
                          (int y,
813
                           unsigned short*& outWritePointerRight,
814
                           unsigned short*& outWritePointerLeft,
815
                           size_t& outPixelsToCopySSE,
816
                           size_t& outPixelsToCopyNormal) const
817
0
{
818
0
   getWritePointer<TYPE>(y,outWritePointerRight,outPixelsToCopySSE,outPixelsToCopyNormal,0);
819
   
820
   
821
0
   if(outWritePointerRight)
822
0
   {
823
0
       getWritePointer<TYPE>(y,outWritePointerLeft,outPixelsToCopySSE,outPixelsToCopyNormal,1);
824
0
   }
825
   
826
0
}
827
828
void
829
LineBufferTaskIIF::execute()
830
0
{
831
0
    try
832
0
    {
833
        //
834
        // Uncompress the data, if necessary
835
        //
836
        
837
0
        if (_lineBuffer->uncompressedData == 0)
838
0
        {
839
0
            int uncompressedSize = 0;
840
0
            int maxY = min (_lineBuffer->maxY, _ifd->maxY);
841
            
842
0
            for (int i = _lineBuffer->minY - _ifd->minY;
843
0
            i <= maxY - _ifd->minY;
844
0
            ++i)
845
0
            {
846
0
                uncompressedSize += (int) _ifd->bytesPerLine[i];
847
0
            }
848
            
849
0
            if (_lineBuffer->compressor &&
850
0
                _lineBuffer->dataSize < uncompressedSize)
851
0
            {
852
0
                _lineBuffer->format = _lineBuffer->compressor->format();
853
                
854
0
                _lineBuffer->dataSize =
855
0
                _lineBuffer->compressor->uncompress (_lineBuffer->buffer,
856
0
                                                     _lineBuffer->dataSize,
857
0
                                                     _lineBuffer->minY,
858
0
                                                     _lineBuffer->uncompressedData);
859
0
            }
860
0
            else
861
0
            {
862
                //
863
                // If the line is uncompressed, it's in XDR format,
864
                // regardless of the compressor's output format.
865
                //
866
                
867
0
                _lineBuffer->format = Compressor::XDR;
868
0
                _lineBuffer->uncompressedData = _lineBuffer->buffer;
869
0
            }
870
0
        }
871
        
872
0
        int yStart, yStop, dy;
873
        
874
0
        if (_ifd->lineOrder == INCREASING_Y)
875
0
        {
876
0
            yStart = _scanLineMin;
877
0
            yStop = _scanLineMax + 1;
878
0
            dy = 1;
879
0
        }
880
0
        else
881
0
        {
882
0
            yStart = _scanLineMax;
883
0
            yStop = _scanLineMin - 1;
884
0
            dy = -1;
885
0
        }
886
        
887
0
        for (int y = yStart; y != yStop; y += dy)
888
0
        {
889
0
            if (modp (y, _optimizationMode._ySampling) != 0)
890
0
                continue;
891
            
892
            //
893
            // Convert one scan line's worth of pixel data back
894
            // from the machine-independent representation, and
895
            // store the result in the frame buffer.
896
            //
897
            
898
            // Set the readPtr to read at the start of uncompressedData
899
            // but with an offet based on calculated array.
900
            // _ifd->offsetInLineBuffer contains offsets based on which
901
            // line we are currently processing.
902
            // Stride will be taken into consideration later.
903
                
904
                
905
0
            const char* readPtr = _lineBuffer->uncompressedData +
906
0
            _ifd->offsetInLineBuffer[y - _ifd->minY];
907
            
908
0
            size_t pixelsToCopySSE = 0;
909
0
            size_t pixelsToCopyNormal = 0;
910
            
911
0
            unsigned short* writePtrLeft = 0;
912
0
            unsigned short* writePtrRight = 0;
913
            
914
0
            size_t channels = _ifd->optimizationData.size();
915
       
916
0
            if(channels>4)
917
0
            {
918
0
                getWritePointerStereo<half>(y, writePtrRight, writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
919
0
            }
920
0
            else 
921
0
            {
922
0
                getWritePointer<half>(y, writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
923
0
            }
924
            
925
0
            if (writePtrRight == 0 && pixelsToCopySSE == 0 && pixelsToCopyNormal == 0)
926
0
            {
927
0
                continue;
928
0
            }
929
            
930
            
931
            //
932
            // support reading up to eight channels
933
            //
934
0
            unsigned short* readPointers[8];
935
            
936
0
            for (size_t i = 0; i < channels ; ++i)
937
0
            {
938
0
                readPointers[i] = (unsigned short*)readPtr + (_ifd->optimizationData[i].offset * (pixelsToCopySSE * 8 + pixelsToCopyNormal));
939
0
            }
940
            
941
            //RGB only
942
0
            if(channels==3 || channels == 6 )
943
0
            {
944
0
                    optimizedWriteToRGB(readPointers[0], readPointers[1], readPointers[2], writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
945
                  
946
                    //stereo RGB
947
0
                    if( channels == 6)
948
0
                    {
949
0
                        optimizedWriteToRGB(readPointers[3], readPointers[4], readPointers[5], writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
950
0
                    }                
951
            //RGBA
952
0
            }else if(channels==4 || channels==8)
953
0
            {
954
                
955
0
                if(_ifd->optimizationData[3].fill)
956
0
                {
957
0
                    optimizedWriteToRGBAFillA(readPointers[0], readPointers[1], readPointers[2], _ifd->optimizationData[3].fillValue.bits() , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
958
0
                }else{
959
0
                    optimizedWriteToRGBA(readPointers[0], readPointers[1], readPointers[2], readPointers[3] , writePtrRight, pixelsToCopySSE, pixelsToCopyNormal);
960
0
                }
961
                
962
                //stereo RGBA
963
0
                if( channels == 8)
964
0
                {
965
0
                    if(_ifd->optimizationData[7].fill)
966
0
                    {
967
0
                        optimizedWriteToRGBAFillA(readPointers[4], readPointers[5], readPointers[6], _ifd->optimizationData[7].fillValue.bits() , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
968
0
                    }else{
969
0
                        optimizedWriteToRGBA(readPointers[4], readPointers[5], readPointers[6], readPointers[7] , writePtrLeft, pixelsToCopySSE, pixelsToCopyNormal);
970
0
                    }
971
0
                }
972
0
            }
973
0
            else {
974
0
                throw(IEX_NAMESPACE::LogicExc("IIF mode called with incorrect channel pattern"));
975
0
            }
976
            
977
            // If we are in NO_OPTIMIZATION mode, this class will never
978
            // get instantiated, so no need to check for it and duplicate
979
            // the code.
980
0
        }
981
0
    }
982
0
    catch (std::exception &e)
983
0
    {
984
0
        if (!_lineBuffer->hasException)
985
0
        {
986
0
            _lineBuffer->exception = e.what();
987
0
            _lineBuffer->hasException = true;
988
0
        }
989
0
    }
990
0
    catch (...)
991
0
    {
992
0
        if (!_lineBuffer->hasException)
993
0
        {
994
0
            _lineBuffer->exception = "unrecognized exception";
995
0
            _lineBuffer->hasException = true;
996
0
        }
997
0
    }
998
0
}
999
#endif
1000
1001
1002
Task *
1003
newLineBufferTask (TaskGroup *group,
1004
                   InputStreamMutex *streamData,
1005
                   ScanLineInputFile::Data *ifd,
1006
                   int number,
1007
                   int scanLineMin,
1008
                   int scanLineMax,
1009
                   OptimizationMode optimizationMode)
1010
0
{
1011
     //
1012
     // Wait for a line buffer to become available, fill the line
1013
     // buffer with raw data from the file if necessary, and create
1014
     // a new LineBufferTask whose execute() method will uncompress
1015
     // the contents of the buffer and copy the pixels into the
1016
     // frame buffer.
1017
     //
1018
     
1019
0
     LineBuffer *lineBuffer = ifd->getLineBuffer (number);
1020
     
1021
0
     try
1022
0
     {
1023
0
         lineBuffer->wait ();
1024
         
1025
0
         if (lineBuffer->number != number)
1026
0
         {
1027
0
             lineBuffer->minY = ifd->minY + number * ifd->linesInBuffer;
1028
0
             lineBuffer->maxY = lineBuffer->minY + ifd->linesInBuffer - 1;
1029
             
1030
0
             lineBuffer->number = number;
1031
0
             lineBuffer->uncompressedData = 0;
1032
             
1033
0
             readPixelData (streamData, ifd, lineBuffer->minY,
1034
0
                            lineBuffer->buffer,
1035
0
                            lineBuffer->dataSize);
1036
0
         }
1037
0
     }
1038
0
     catch (std::exception &e)
1039
0
     {
1040
0
         if (!lineBuffer->hasException)
1041
0
         {
1042
0
             lineBuffer->exception = e.what();
1043
0
             lineBuffer->hasException = true;
1044
0
         }
1045
0
         lineBuffer->number = -1;
1046
0
         lineBuffer->post();
1047
0
         throw;
1048
0
     }
1049
0
     catch (...)
1050
0
     {
1051
         //
1052
         // Reading from the file caused an exception.
1053
         // Signal that the line buffer is free, and
1054
         // re-throw the exception.
1055
         //
1056
         
1057
0
         lineBuffer->exception = "unrecognized exception";
1058
0
         lineBuffer->hasException = true;
1059
0
         lineBuffer->number = -1;
1060
0
         lineBuffer->post();
1061
0
         throw;
1062
0
     }
1063
     
1064
0
     scanLineMin = max (lineBuffer->minY, scanLineMin);
1065
0
     scanLineMax = min (lineBuffer->maxY, scanLineMax);
1066
     
1067
     
1068
0
     Task* retTask = 0;
1069
     
1070
0
#ifdef IMF_HAVE_SSE2     
1071
0
     if (optimizationMode._optimizable)
1072
0
     {
1073
         
1074
0
         retTask = new LineBufferTaskIIF (group, ifd, lineBuffer,
1075
0
                                          scanLineMin, scanLineMax,
1076
0
                                          optimizationMode);
1077
      
1078
0
     }
1079
0
     else
1080
0
#endif         
1081
0
     {
1082
0
         retTask = new LineBufferTask (group, ifd, lineBuffer,
1083
0
                                       scanLineMin, scanLineMax,
1084
0
                                       optimizationMode);
1085
0
     }
1086
     
1087
0
     return retTask;
1088
     
1089
0
 }
1090
 
1091
  
1092
1093
1094
} // namespace
1095
1096
1097
void ScanLineInputFile::initialize(const Header& header)
1098
0
{
1099
0
    try
1100
0
    {
1101
0
        _data->header = header;
1102
1103
0
        _data->lineOrder = _data->header.lineOrder();
1104
1105
0
        const Box2i &dataWindow = _data->header.dataWindow();
1106
1107
0
        _data->minX = dataWindow.min.x;
1108
0
        _data->maxX = dataWindow.max.x;
1109
0
        _data->minY = dataWindow.min.y;
1110
0
        _data->maxY = dataWindow.max.y;
1111
1112
0
        size_t maxBytesPerLine = bytesPerLineTable (_data->header,
1113
0
                                                    _data->bytesPerLine);
1114
1115
0
        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1116
0
        {
1117
0
            _data->lineBuffers[i] = new LineBuffer (newCompressor
1118
0
                                                (_data->header.compression(),
1119
0
                                                 maxBytesPerLine,
1120
0
                                                 _data->header));
1121
0
        }
1122
1123
0
        _data->linesInBuffer =
1124
0
            numLinesInBuffer (_data->lineBuffers[0]->compressor);
1125
1126
0
        _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
1127
1128
0
        if (!_streamData->is->isMemoryMapped())
1129
0
        {
1130
0
            for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1131
0
            {
1132
0
                _data->lineBuffers[i]->buffer = (char *) EXRAllocAligned(_data->lineBufferSize*sizeof(char),16);
1133
0
            }
1134
0
        }
1135
0
        _data->nextLineBufferMinY = _data->minY - 1;
1136
1137
0
        offsetInLineBufferTable (_data->bytesPerLine,
1138
0
                                 _data->linesInBuffer,
1139
0
                                 _data->offsetInLineBuffer);
1140
1141
0
        int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
1142
0
                              _data->linesInBuffer) / _data->linesInBuffer;
1143
1144
0
        _data->lineOffsets.resize (lineOffsetSize);
1145
0
    }
1146
0
    catch (...)
1147
0
    {
1148
0
        delete _data;
1149
0
        _data=NULL;
1150
0
        throw;
1151
0
    }
1152
0
}
1153
1154
1155
ScanLineInputFile::ScanLineInputFile(InputPartData* part)
1156
0
{
1157
0
    if (part->header.type() != SCANLINEIMAGE)
1158
0
        throw IEX_NAMESPACE::ArgExc("Can't build a ScanLineInputFile from a type-mismatched part.");
1159
1160
0
    _data = new Data(part->numThreads);
1161
0
    _streamData = part->mutex;
1162
0
    _data->memoryMapped = _streamData->is->isMemoryMapped();
1163
1164
0
    _data->version = part->version;
1165
1166
0
    initialize(part->header);
1167
1168
0
    _data->lineOffsets = part->chunkOffsets;
1169
1170
0
    _data->partNumber = part->partNumber;
1171
    //
1172
    // (TODO) change this code later.
1173
    // The completeness of the file should be detected in MultiPartInputFile.
1174
    //
1175
0
    _data->fileIsComplete = true;
1176
0
}
1177
1178
1179
ScanLineInputFile::ScanLineInputFile
1180
    (const Header &header,
1181
     OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
1182
     int numThreads)
1183
:
1184
    _data (new Data (numThreads)),
1185
    _streamData (new InputStreamMutex())
1186
0
{
1187
0
    _streamData->is = is;
1188
0
    _data->memoryMapped = is->isMemoryMapped();
1189
1190
0
    initialize(header);
1191
    
1192
    //
1193
    // (TODO) this is nasty - we need a better way of working out what type of file has been used.
1194
    // in any case I believe this constructor only gets used with single part files
1195
    // and 'version' currently only tracks multipart state, so setting to 0 (not multipart) works for us
1196
    //
1197
    
1198
0
    _data->version=0;
1199
0
    readLineOffsets (*_streamData->is,
1200
0
                     _data->lineOrder,
1201
0
                     _data->lineOffsets,
1202
0
                     _data->fileIsComplete);
1203
0
}
1204
1205
1206
ScanLineInputFile::~ScanLineInputFile ()
1207
0
{
1208
0
    if (!_data->memoryMapped)
1209
0
    {
1210
0
        for (size_t i = 0; i < _data->lineBuffers.size(); i++)
1211
0
        {
1212
0
            EXRFreeAligned(_data->lineBuffers[i]->buffer);
1213
0
        }
1214
0
    }
1215
            
1216
1217
    //
1218
    // ScanLineInputFile should never delete the stream,
1219
    // because it does not own the stream.
1220
    // We just delete the Mutex here.
1221
    //
1222
0
    if (_data->partNumber == -1)
1223
0
        delete _streamData;
1224
1225
0
    delete _data;
1226
0
}
1227
1228
1229
const char *
1230
ScanLineInputFile::fileName () const
1231
0
{
1232
0
    return _streamData->is->fileName();
1233
0
}
1234
1235
1236
const Header &
1237
ScanLineInputFile::header () const
1238
0
{
1239
0
    return _data->header;
1240
0
}
1241
1242
1243
int
1244
ScanLineInputFile::version () const
1245
0
{
1246
0
    return _data->version;
1247
0
}
1248
1249
1250
namespace
1251
{
1252
    
1253
    
1254
// returns the optimization state for the given arrangement of frame bufers
1255
// this assumes:
1256
//   both the file and framebuffer are half float data
1257
//   both the file and framebuffer have xSampling and ySampling=1
1258
//   entries in optData are sorted into their interleave order (i.e. by base address)
1259
//   These tests are done by SetFrameBuffer as it is building optData
1260
//  
1261
OptimizationMode
1262
detectOptimizationMode (const vector<sliceOptimizationData>& optData)
1263
0
{
1264
0
    OptimizationMode w;
1265
    
1266
    // need to be compiled with SSE optimisations: if not, just returns false
1267
0
#if IMF_HAVE_SSE2
1268
    
1269
    
1270
    // only handle reading 3,4,6 or 8 channels
1271
0
    switch(optData.size())
1272
0
    {
1273
0
        case 3 : break;
1274
0
        case 4 : break;
1275
0
        case 6 : break;
1276
0
        case 8 : break;
1277
0
        default :
1278
0
            return w;
1279
0
    }
1280
    
1281
    //
1282
    // the point at which data switches between the primary and secondary bank
1283
    //
1284
0
    size_t bankSize = optData.size()>4 ? optData.size()/2 : optData.size();
1285
    
1286
0
    for(size_t i=0;i<optData.size();i++)
1287
0
    {
1288
0
        const sliceOptimizationData& data = optData[i];
1289
        // can't fill anything other than channel 3 or channel 7
1290
0
        if(data.fill)
1291
0
        {
1292
0
            if(i!=3 && i!=7)
1293
0
            {
1294
0
                return w;
1295
0
            }
1296
0
        }
1297
        
1298
        // cannot have gaps in the channel layout, so the stride must be (number of channels written in the bank)*2
1299
0
        if(data.xStride !=bankSize*2)
1300
0
        {
1301
0
            return w;
1302
0
        }
1303
        
1304
        // each bank of channels must be channel interleaved: each channel base pointer must be (previous channel+2)
1305
        // this also means channel sampling pattern must be consistent, as must yStride
1306
0
        if(i!=0 && i!=bankSize)
1307
0
        {
1308
0
            if(data.base!=optData[i-1].base+2)
1309
0
            {
1310
0
                return w;
1311
0
            }
1312
0
        }
1313
0
        if(i!=0)
1314
0
        {
1315
            
1316
0
            if(data.yStride!=optData[i-1].yStride)
1317
0
            {
1318
0
                return w;
1319
0
            }
1320
0
        }
1321
0
    }
1322
    
1323
1324
0
    w._ySampling=optData[0].ySampling;
1325
0
    w._optimizable=true;
1326
    
1327
0
#endif
1328
1329
0
    return w;
1330
0
}
1331
1332
1333
} // Anonymous namespace
1334
1335
void  
1336
ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1337
0
{
1338
0
    Lock lock (*_streamData);
1339
1340
    
1341
    
1342
0
    const ChannelList &channels = _data->header.channels();
1343
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1344
0
   j != frameBuffer.end();
1345
0
   ++j)
1346
0
    {
1347
0
  ChannelList::ConstIterator i = channels.find (j.name());
1348
1349
0
  if (i == channels.end())
1350
0
      continue;
1351
1352
0
  if (i.channel().xSampling != j.slice().xSampling ||
1353
0
      i.channel().ySampling != j.slice().ySampling)
1354
0
      THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1355
0
        "of \"" << i.name() << "\" channel "
1356
0
        "of input file \"" << fileName() << "\" are "
1357
0
        "not compatible with the frame buffer's "
1358
0
        "subsampling factors.");
1359
0
    }
1360
1361
    // optimization is possible if this is a little endian system
1362
    // and both inputs and outputs are half floats
1363
    // 
1364
0
    bool optimizationPossible = true;
1365
    
1366
0
    if (!GLOBAL_SYSTEM_LITTLE_ENDIAN)
1367
0
    {
1368
0
        optimizationPossible =false;
1369
0
    }
1370
    
1371
0
    vector<sliceOptimizationData> optData;
1372
    
1373
1374
    //
1375
    // Initialize the slice table for readPixels().
1376
    //
1377
1378
0
    vector<InSliceInfo> slices;
1379
0
    ChannelList::ConstIterator i = channels.begin();
1380
    
1381
    // current offset of channel: pixel data starts at offset*width into the
1382
    // decompressed scanline buffer
1383
0
    size_t offset = 0;
1384
    
1385
0
    for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1386
0
   j != frameBuffer.end();
1387
0
   ++j)
1388
0
    {
1389
0
  while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1390
0
  {
1391
      //
1392
      // Channel i is present in the file but not
1393
      // in the frame buffer; data for channel i
1394
      // will be skipped during readPixels().
1395
      //
1396
1397
0
      slices.push_back (InSliceInfo (i.channel().type,
1398
0
             i.channel().type,
1399
0
             0, // base
1400
0
             0, // xStride
1401
0
             0, // yStride
1402
0
             i.channel().xSampling,
1403
0
             i.channel().ySampling,
1404
0
             false,  // fill
1405
0
             true, // skip
1406
0
             0.0)); // fillValue
1407
      
1408
0
              switch(i.channel().type)
1409
0
              {
1410
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1411
0
                      offset++;
1412
0
                      break;
1413
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1414
0
                      offset+=2;
1415
0
                      break;
1416
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1417
0
                      offset+=2;
1418
0
                      break;
1419
0
              }
1420
0
              ++i;
1421
0
  }
1422
1423
0
  bool fill = false;
1424
1425
0
  if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1426
0
  {
1427
      //
1428
      // Channel i is present in the frame buffer, but not in the file.
1429
      // In the frame buffer, slice j will be filled with a default value.
1430
      //
1431
1432
0
      fill = true;
1433
0
  }
1434
1435
0
  slices.push_back (InSliceInfo (j.slice().type,
1436
0
               fill? j.slice().type:
1437
0
                     i.channel().type,
1438
0
               j.slice().base,
1439
0
               j.slice().xStride,
1440
0
               j.slice().yStride,
1441
0
               j.slice().xSampling,
1442
0
               j.slice().ySampling,
1443
0
               fill,
1444
0
               false, // skip
1445
0
               j.slice().fillValue));
1446
1447
0
          if(!fill && i.channel().type!=OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1448
0
          {
1449
0
              optimizationPossible = false;
1450
0
          }
1451
          
1452
0
          if(j.slice().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF)
1453
0
          {
1454
0
              optimizationPossible = false;
1455
0
          }
1456
0
          if(j.slice().xSampling!=1 || j.slice().ySampling!=1)
1457
0
          {
1458
0
              optimizationPossible = false;
1459
0
          }
1460
1461
          
1462
0
          if(optimizationPossible)
1463
0
          {
1464
0
              sliceOptimizationData dat;
1465
0
              dat.base = j.slice().base;
1466
0
              dat.fill = fill;
1467
0
              dat.fillValue = j.slice().fillValue;
1468
0
              dat.offset = offset;
1469
0
              dat.xStride = j.slice().xStride;
1470
0
              dat.yStride = j.slice().yStride;
1471
0
              dat.xSampling = j.slice().xSampling;
1472
0
              dat.ySampling = j.slice().ySampling;
1473
0
              optData.push_back(dat);
1474
0
          }
1475
          
1476
0
          if(!fill)
1477
0
          {
1478
0
              switch(i.channel().type)
1479
0
              {
1480
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1481
0
                      offset++;
1482
0
                      break;
1483
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1484
0
                      offset+=2;
1485
0
                      break;
1486
0
                  case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1487
0
                      offset+=2;
1488
0
                      break;
1489
0
              }
1490
0
          }
1491
          
1492
1493
          
1494
0
  if (i != channels.end() && !fill)
1495
0
      ++i;
1496
0
    }
1497
1498
   
1499
0
   if(optimizationPossible)
1500
0
   {
1501
       //
1502
       // check optimisibility
1503
       // based on channel ordering and fill channel positions
1504
       //
1505
0
       sort(optData.begin(),optData.end());
1506
0
       _data->optimizationMode = detectOptimizationMode(optData);
1507
0
   }
1508
   
1509
0
   if(!optimizationPossible || _data->optimizationMode._optimizable==false)
1510
0
   {   
1511
0
       optData = vector<sliceOptimizationData>();
1512
0
       _data->optimizationMode._optimizable=false;
1513
0
   }
1514
    
1515
    //
1516
    // Store the new frame buffer.
1517
    //
1518
1519
0
    _data->frameBuffer = frameBuffer;
1520
0
    _data->slices = slices;
1521
0
    _data->optimizationData = optData;
1522
0
}
1523
1524
1525
const FrameBuffer &
1526
ScanLineInputFile::frameBuffer () const
1527
0
{
1528
0
    Lock lock (*_streamData);
1529
0
    return _data->frameBuffer;
1530
0
}
1531
1532
1533
bool
1534
ScanLineInputFile::isComplete () const
1535
0
{
1536
0
    return _data->fileIsComplete;
1537
0
}
1538
1539
bool ScanLineInputFile::isOptimizationEnabled() const
1540
0
{
1541
0
    if (_data->slices.size() == 0)
1542
0
        throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1543
0
        "as pixel data destination.");
1544
    
1545
0
    return _data->optimizationMode._optimizable;
1546
0
}
1547
1548
1549
void  
1550
ScanLineInputFile::readPixels (int scanLine1, int scanLine2)
1551
0
{
1552
0
    try
1553
0
    {
1554
0
        Lock lock (*_streamData);
1555
1556
0
  if (_data->slices.size() == 0)
1557
0
      throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1558
0
             "as pixel data destination.");
1559
1560
0
  int scanLineMin = min (scanLine1, scanLine2);
1561
0
  int scanLineMax = max (scanLine1, scanLine2);
1562
1563
0
  if (scanLineMin < _data->minY || scanLineMax > _data->maxY)
1564
0
      throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1565
0
             "the image file's data window.");
1566
1567
        //
1568
        // We impose a numbering scheme on the lineBuffers where the first
1569
        // scanline is contained in lineBuffer 1.
1570
        //
1571
        // Determine the first and last lineBuffer numbers in this scanline
1572
        // range. We always attempt to read the scanlines in the order that
1573
        // they are stored in the file.
1574
        //
1575
1576
0
        int start, stop, dl;
1577
1578
0
        if (_data->lineOrder == INCREASING_Y)
1579
0
        {
1580
0
            start = (scanLineMin - _data->minY) / _data->linesInBuffer;
1581
0
            stop  = (scanLineMax - _data->minY) / _data->linesInBuffer + 1;
1582
0
            dl = 1;
1583
0
        }
1584
0
        else
1585
0
        {
1586
0
            start = (scanLineMax - _data->minY) / _data->linesInBuffer;
1587
0
            stop  = (scanLineMin - _data->minY) / _data->linesInBuffer - 1;
1588
0
            dl = -1;
1589
0
        }
1590
1591
        //
1592
        // Create a task group for all line buffer tasks.  When the
1593
  // task group goes out of scope, the destructor waits until
1594
  // all tasks are complete.
1595
        //
1596
        
1597
0
        {
1598
0
            TaskGroup taskGroup;
1599
    
1600
            //
1601
            // Add the line buffer tasks.
1602
            //
1603
            // The tasks will execute in the order that they are created
1604
            // because we lock the line buffers during construction and the
1605
            // constructors are called by the main thread.  Hence, in order
1606
      // for a successive task to execute the previous task which
1607
      // used that line buffer must have completed already.
1608
            //
1609
    
1610
0
            for (int l = start; l != stop; l += dl)
1611
0
            {
1612
0
                ThreadPool::addGlobalTask (newLineBufferTask (&taskGroup,
1613
0
                                                              _streamData,
1614
0
                                                              _data, l,
1615
0
                                                              scanLineMin,
1616
0
                                                              scanLineMax,
1617
0
                                                              _data->optimizationMode));
1618
0
            }
1619
        
1620
      //
1621
            // finish all tasks
1622
      //
1623
0
        }
1624
        
1625
  //
1626
  // Exeption handling:
1627
  //
1628
  // LineBufferTask::execute() may have encountered exceptions, but
1629
  // those exceptions occurred in another thread, not in the thread
1630
  // that is executing this call to ScanLineInputFile::readPixels().
1631
  // LineBufferTask::execute() has caught all exceptions and stored
1632
  // the exceptions' what() strings in the line buffers.
1633
  // Now we check if any line buffer contains a stored exception; if
1634
  // this is the case then we re-throw the exception in this thread.
1635
  // (It is possible that multiple line buffers contain stored
1636
  // exceptions.  We re-throw the first exception we find and
1637
  // ignore all others.)
1638
  //
1639
1640
0
  const string *exception = 0;
1641
1642
0
        for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
1643
0
  {
1644
0
            LineBuffer *lineBuffer = _data->lineBuffers[i];
1645
1646
0
      if (lineBuffer->hasException && !exception)
1647
0
    exception = &lineBuffer->exception;
1648
1649
0
      lineBuffer->hasException = false;
1650
0
  }
1651
1652
0
  if (exception)
1653
0
      throw IEX_NAMESPACE::IoExc (*exception);
1654
0
    }
1655
0
    catch (IEX_NAMESPACE::BaseExc &e)
1656
0
    {
1657
0
  REPLACE_EXC (e, "Error reading pixel data from image "
1658
0
            "file \"" << fileName() << "\". " << e);
1659
0
  throw;
1660
0
    }
1661
0
}
1662
1663
1664
void  
1665
ScanLineInputFile::readPixels (int scanLine)
1666
0
{
1667
0
    readPixels (scanLine, scanLine);
1668
0
}
1669
1670
1671
void
1672
ScanLineInputFile::rawPixelData (int firstScanLine,
1673
         const char *&pixelData,
1674
         int &pixelDataSize)
1675
0
{
1676
0
    try
1677
0
    {
1678
0
        Lock lock (*_streamData);
1679
1680
0
  if (firstScanLine < _data->minY || firstScanLine > _data->maxY)
1681
0
  {
1682
0
      throw IEX_NAMESPACE::ArgExc ("Tried to read scan line outside "
1683
0
             "the image file's data window.");
1684
0
  }
1685
1686
0
        int minY = lineBufferMinY
1687
0
      (firstScanLine, _data->minY, _data->linesInBuffer);
1688
1689
0
  readPixelData
1690
0
      (_streamData, _data, minY, _data->lineBuffers[0]->buffer, pixelDataSize);
1691
1692
0
  pixelData = _data->lineBuffers[0]->buffer;
1693
0
    }
1694
0
    catch (IEX_NAMESPACE::BaseExc &e)
1695
0
    {
1696
0
  REPLACE_EXC (e, "Error reading pixel data from image "
1697
0
            "file \"" << fileName() << "\". " << e);
1698
0
  throw;
1699
0
    }
1700
0
}
1701
1702
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT