Coverage Report

Created: 2025-06-13 06:50

/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
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
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
0
    Task (group),
494
0
    _ifd (ifd),
495
0
    _lineBuffer (lineBuffer),
496
0
    _scanLineMin (scanLineMin),
497
0
    _scanLineMax (scanLineMax),
498
0
    _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
0
     Task (group),
716
0
     _ifd (ifd),
717
0
     _lineBuffer (lineBuffer),
718
0
     _scanLineMin (scanLineMin),
719
0
     _scanLineMax (scanLineMax),
720
0
     _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
0
    _data (new Data (numThreads)),
1185
0
    _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