~paparazzi-uav/paparazzi/v5.0-manual

« back to all changes in this revision

Viewing changes to sw/ext/opencv_bebop/opencv/3rdparty/openexr/IlmImf/ImfOutputFile.cpp

  • Committer: Paparazzi buildbot
  • Date: 2016-05-18 15:00:29 UTC
  • Revision ID: felix.ruess+docbot@gmail.com-20160518150029-e8lgzi5kvb4p7un9
Manual import commit 4b8bbb730080dac23cf816b98908dacfabe2a8ec from v5.0 branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 OutputFile
 
39
//
 
40
//-----------------------------------------------------------------------------
 
41
 
 
42
#include <ImfOutputFile.h>
 
43
#include <ImfInputFile.h>
 
44
#include <ImfChannelList.h>
 
45
#include <ImfMisc.h>
 
46
#include <ImfStdIO.h>
 
47
#include <ImfCompressor.h>
 
48
#include "ImathBox.h"
 
49
#include "ImathFun.h"
 
50
#include <ImfArray.h>
 
51
#include <ImfXdr.h>
 
52
#include <ImfPreviewImageAttribute.h>
 
53
#include "IlmThreadPool.h"
 
54
#include "IlmThreadSemaphore.h"
 
55
#include "IlmThreadMutex.h"
 
56
#include "Iex.h"
 
57
#include <string>
 
58
#include <vector>
 
59
#include <fstream>
 
60
#include <assert.h>
 
61
#include <algorithm> // for std::max()
 
62
 
 
63
 
 
64
namespace Imf {
 
65
 
 
66
using Imath::Box2i;
 
67
using Imath::divp;
 
68
using Imath::modp;
 
69
using std::string;
 
70
using std::vector;
 
71
using std::ofstream;
 
72
using std::min;
 
73
using std::max;
 
74
using IlmThread::Mutex;
 
75
using IlmThread::Lock;
 
76
using IlmThread::Semaphore;
 
77
using IlmThread::Task;
 
78
using IlmThread::TaskGroup;
 
79
using IlmThread::ThreadPool;
 
80
 
 
81
namespace {
 
82
 
 
83
 
 
84
struct OutSliceInfo
 
85
{
 
86
    PixelType           type;
 
87
    const char *        base;
 
88
    size_t              xStride;
 
89
    size_t              yStride;
 
90
    int                 xSampling;
 
91
    int                 ySampling;
 
92
    bool                zero;
 
93
 
 
94
    OutSliceInfo (PixelType type = HALF,
 
95
              const char *base = 0,
 
96
              size_t xStride = 0,
 
97
              size_t yStride = 0,
 
98
              int xSampling = 1,
 
99
              int ySampling = 1,
 
100
              bool zero = false);
 
101
};
 
102
 
 
103
 
 
104
OutSliceInfo::OutSliceInfo (PixelType t,
 
105
                    const char *b,
 
106
                    size_t xs, size_t ys,
 
107
                    int xsm, int ysm,
 
108
                    bool z)
 
109
:
 
110
    type (t),
 
111
    base (b),
 
112
    xStride (xs),
 
113
    yStride (ys),
 
114
    xSampling (xsm),
 
115
    ySampling (ysm),
 
116
    zero (z)
 
117
{
 
118
    // empty
 
119
}
 
120
 
 
121
 
 
122
struct LineBuffer
 
123
{
 
124
    Array<char>         buffer;
 
125
    const char *        dataPtr;
 
126
    int                 dataSize;
 
127
    char *              endOfLineBufferData;
 
128
    int                 minY;
 
129
    int                 maxY;
 
130
    int                 scanLineMin;
 
131
    int                 scanLineMax;
 
132
    Compressor *        compressor;
 
133
    bool                partiallyFull;        // has incomplete data
 
134
    bool                hasException;
 
135
    string              exception;
 
136
 
 
137
    LineBuffer (Compressor *comp);
 
138
    ~LineBuffer ();
 
139
 
 
140
    void                wait () {_sem.wait();}
 
141
    void                post () {_sem.post();}
 
142
 
 
143
  private:
 
144
 
 
145
    Semaphore           _sem;
 
146
};
 
147
 
 
148
 
 
149
LineBuffer::LineBuffer (Compressor *comp) :
 
150
    dataPtr (0),
 
151
    dataSize (0),
 
152
    compressor (comp),
 
153
    partiallyFull (false),
 
154
    hasException (false),
 
155
    exception (),
 
156
    _sem (1)
 
157
{
 
158
    // empty
 
159
}
 
160
 
 
161
 
 
162
LineBuffer::~LineBuffer ()
 
163
{
 
164
    delete compressor;
 
165
}
 
166
 
 
167
} // namespace
 
168
 
 
169
 
 
170
struct OutputFile::Data: public Mutex
 
171
{
 
172
    Header               header;                // the image header
 
173
    int                  version;               // file format version
 
174
    Int64                previewPosition;       // file position for preview
 
175
    FrameBuffer          frameBuffer;           // framebuffer to write into
 
176
    int                  currentScanLine;       // next scanline to be written
 
177
    int                  missingScanLines;      // number of lines to write
 
178
    LineOrder            lineOrder;             // the file's lineorder
 
179
    int                  minX;                  // data window's min x coord
 
180
    int                  maxX;                  // data window's max x coord
 
181
    int                  minY;                  // data window's min y coord
 
182
    int                  maxY;                  // data window's max x coord
 
183
    vector<Int64>        lineOffsets;           // stores offsets in file for
 
184
                        // each scanline
 
185
    vector<size_t>       bytesPerLine;          // combined size of a line over
 
186
                                                // all channels
 
187
    vector<size_t>       offsetInLineBuffer;    // offset for each scanline in
 
188
                                                // its linebuffer
 
189
    Compressor::Format   format;                // compressor's data format
 
190
    vector<OutSliceInfo> slices;                // info about channels in file
 
191
    OStream *            os;                    // file stream to write to
 
192
    bool                 deleteStream;
 
193
    Int64                lineOffsetsPosition;   // file position for line
 
194
                                                // offset table
 
195
    Int64                currentPosition;       // current file position
 
196
 
 
197
    vector<LineBuffer*>  lineBuffers;           // each holds one line buffer
 
198
    int                  linesInBuffer;         // number of scanlines each
 
199
                                                // buffer holds
 
200
    size_t               lineBufferSize;        // size of the line buffer
 
201
 
 
202
     Data (bool deleteStream, int numThreads);
 
203
    ~Data ();
 
204
 
 
205
 
 
206
    inline LineBuffer * getLineBuffer (int number); // hash function from line
 
207
                                // buffer indices into our
 
208
                            // vector of line buffers
 
209
};
 
210
 
 
211
 
 
212
OutputFile::Data::Data (bool deleteStream, int numThreads):
 
213
    os (0),
 
214
    deleteStream (deleteStream),
 
215
    lineOffsetsPosition (0)
 
216
{
 
217
    //
 
218
    // We need at least one lineBuffer, but if threading is used,
 
219
    // to keep n threads busy we need 2*n lineBuffers.
 
220
    //
 
221
 
 
222
    lineBuffers.resize (max (1, 2 * numThreads));
 
223
}
 
224
 
 
225
 
 
226
OutputFile::Data::~Data ()
 
227
{
 
228
    if (deleteStream)
 
229
    delete os;
 
230
 
 
231
    for (size_t i = 0; i < lineBuffers.size(); i++)
 
232
        delete lineBuffers[i];
 
233
}
 
234
 
 
235
 
 
236
LineBuffer*
 
237
OutputFile::Data::getLineBuffer (int number)
 
238
{
 
239
    return lineBuffers[number % lineBuffers.size()];
 
240
}
 
241
 
 
242
 
 
243
namespace {
 
244
 
 
245
 
 
246
Int64
 
247
writeLineOffsets (OStream &os, const vector<Int64> &lineOffsets)
 
248
{
 
249
    Int64 pos = os.tellp();
 
250
 
 
251
    if (pos == -1)
 
252
    Iex::throwErrnoExc ("Cannot determine current file position (%T).");
 
253
 
 
254
    for (unsigned int i = 0; i < lineOffsets.size(); i++)
 
255
    Xdr::write <StreamIO> (os, lineOffsets[i]);
 
256
 
 
257
    return pos;
 
258
}
 
259
 
 
260
 
 
261
void
 
262
writePixelData (OutputFile::Data *ofd,
 
263
                int lineBufferMinY,
 
264
        const char pixelData[],
 
265
        int pixelDataSize)
 
266
{
 
267
    //
 
268
    // Store a block of pixel data in the output file, and try
 
269
    // to keep track of the current writing position the file
 
270
    // without calling tellp() (tellp() can be fairly expensive).
 
271
    //
 
272
 
 
273
    Int64 currentPosition = ofd->currentPosition;
 
274
    ofd->currentPosition = 0;
 
275
 
 
276
    if (currentPosition == 0)
 
277
    currentPosition = ofd->os->tellp();
 
278
 
 
279
    ofd->lineOffsets[(ofd->currentScanLine - ofd->minY) / ofd->linesInBuffer] =
 
280
    currentPosition;
 
281
 
 
282
    #ifdef DEBUG
 
283
 
 
284
    assert (ofd->os->tellp() == currentPosition);
 
285
 
 
286
    #endif
 
287
 
 
288
    Xdr::write <StreamIO> (*ofd->os, lineBufferMinY);
 
289
    Xdr::write <StreamIO> (*ofd->os, pixelDataSize);
 
290
    ofd->os->write (pixelData, pixelDataSize);
 
291
 
 
292
    ofd->currentPosition = currentPosition +
 
293
               Xdr::size<int>() +
 
294
               Xdr::size<int>() +
 
295
               pixelDataSize;
 
296
}
 
297
 
 
298
 
 
299
inline void
 
300
writePixelData (OutputFile::Data *ofd, const LineBuffer *lineBuffer)
 
301
{
 
302
    writePixelData (ofd,
 
303
            lineBuffer->minY,
 
304
                    lineBuffer->dataPtr,
 
305
            lineBuffer->dataSize);
 
306
}
 
307
 
 
308
 
 
309
void
 
310
convertToXdr (OutputFile::Data *ofd,
 
311
              Array<char> &lineBuffer,
 
312
              int lineBufferMinY,
 
313
              int lineBufferMaxY,
 
314
              int /*inSize*/)
 
315
{
 
316
    //
 
317
    // Convert the contents of a lineBuffer from the machine's native
 
318
    // representation to Xdr format.  This function is called by
 
319
    // CompressLineBuffer::execute(), below, if the compressor wanted
 
320
    // its input pixel data in the machine's native format, but then
 
321
    // failed to compress the data (most compressors will expand rather
 
322
    // than compress random input data).
 
323
    //
 
324
    // Note that this routine assumes that the machine's native
 
325
    // representation of the pixel data has the same size as the
 
326
    // Xdr representation.  This makes it possible to convert the
 
327
    // pixel data in place, without an intermediate temporary buffer.
 
328
    //
 
329
 
 
330
    int startY, endY;           // The first and last scanlines in
 
331
                    // the file that are in the lineBuffer.
 
332
    int step;
 
333
 
 
334
    if (ofd->lineOrder == INCREASING_Y)
 
335
    {
 
336
    startY = max (lineBufferMinY, ofd->minY);
 
337
    endY = min (lineBufferMaxY, ofd->maxY) + 1;
 
338
        step = 1;
 
339
    }
 
340
    else
 
341
    {
 
342
    startY = min (lineBufferMaxY, ofd->maxY);
 
343
    endY = max (lineBufferMinY, ofd->minY) - 1;
 
344
        step = -1;
 
345
    }
 
346
 
 
347
    //
 
348
    // Iterate over all scanlines in the lineBuffer to convert.
 
349
    //
 
350
 
 
351
    for (int y = startY; y != endY; y += step)
 
352
    {
 
353
    //
 
354
        // Set these to point to the start of line y.
 
355
        // We will write to writePtr from readPtr.
 
356
    //
 
357
 
 
358
        char *writePtr = lineBuffer + ofd->offsetInLineBuffer[y - ofd->minY];
 
359
        const char *readPtr = writePtr;
 
360
 
 
361
    //
 
362
        // Iterate over all slices in the file.
 
363
    //
 
364
 
 
365
        for (unsigned int i = 0; i < ofd->slices.size(); ++i)
 
366
        {
 
367
            //
 
368
            // Test if scan line y of this channel is
 
369
            // contains any data (the scan line contains
 
370
            // data only if y % ySampling == 0).
 
371
            //
 
372
 
 
373
            const OutSliceInfo &slice = ofd->slices[i];
 
374
 
 
375
            if (modp (y, slice.ySampling) != 0)
 
376
                continue;
 
377
 
 
378
            //
 
379
            // Find the number of sampled pixels, dMaxX-dMinX+1, for
 
380
        // slice i in scan line y (i.e. pixels within the data window
 
381
            // for which x % xSampling == 0).
 
382
            //
 
383
 
 
384
            int dMinX = divp (ofd->minX, slice.xSampling);
 
385
            int dMaxX = divp (ofd->maxX, slice.xSampling);
 
386
 
 
387
        //
 
388
            // Convert the samples in place.
 
389
        //
 
390
 
 
391
            convertInPlace (writePtr, readPtr, slice.type, dMaxX - dMinX + 1);
 
392
        }
 
393
    }
 
394
}
 
395
 
 
396
 
 
397
//
 
398
// A LineBufferTask encapsulates the task of copying a set of scanlines
 
399
// from the user's frame buffer into a LineBuffer object, compressing
 
400
// the data if necessary.
 
401
//
 
402
 
 
403
class LineBufferTask: public Task
 
404
{
 
405
  public:
 
406
 
 
407
    LineBufferTask (TaskGroup *group,
 
408
                    OutputFile::Data *ofd,
 
409
            int number,
 
410
                    int scanLineMin,
 
411
            int scanLineMax);
 
412
 
 
413
    virtual ~LineBufferTask ();
 
414
 
 
415
    virtual void        execute ();
 
416
 
 
417
  private:
 
418
 
 
419
    OutputFile::Data *  _ofd;
 
420
    LineBuffer *        _lineBuffer;
 
421
};
 
422
 
 
423
 
 
424
LineBufferTask::LineBufferTask
 
425
    (TaskGroup *group,
 
426
     OutputFile::Data *ofd,
 
427
     int number,
 
428
     int scanLineMin,
 
429
     int scanLineMax)
 
430
:
 
431
    Task (group),
 
432
    _ofd (ofd),
 
433
    _lineBuffer (_ofd->getLineBuffer(number))
 
434
{
 
435
    //
 
436
    // Wait for the lineBuffer to become available
 
437
    //
 
438
 
 
439
    _lineBuffer->wait ();
 
440
 
 
441
    //
 
442
    // Initialize the lineBuffer data if necessary
 
443
    //
 
444
 
 
445
    if (!_lineBuffer->partiallyFull)
 
446
    {
 
447
        _lineBuffer->endOfLineBufferData = _lineBuffer->buffer;
 
448
 
 
449
        _lineBuffer->minY = _ofd->minY + number * _ofd->linesInBuffer;
 
450
 
 
451
        _lineBuffer->maxY = min (_lineBuffer->minY + _ofd->linesInBuffer - 1,
 
452
                 _ofd->maxY);
 
453
 
 
454
        _lineBuffer->partiallyFull = true;
 
455
    }
 
456
 
 
457
    _lineBuffer->scanLineMin = max (_lineBuffer->minY, scanLineMin);
 
458
    _lineBuffer->scanLineMax = min (_lineBuffer->maxY, scanLineMax);
 
459
}
 
460
 
 
461
 
 
462
LineBufferTask::~LineBufferTask ()
 
463
{
 
464
    //
 
465
    // Signal that the line buffer is now free
 
466
    //
 
467
 
 
468
    _lineBuffer->post ();
 
469
}
 
470
 
 
471
 
 
472
void
 
473
LineBufferTask::execute ()
 
474
{
 
475
    try
 
476
    {
 
477
        //
 
478
        // First copy the pixel data from the
 
479
    // frame buffer into the line buffer
 
480
        //
 
481
 
 
482
        int yStart, yStop, dy;
 
483
 
 
484
        if (_ofd->lineOrder == INCREASING_Y)
 
485
        {
 
486
            yStart = _lineBuffer->scanLineMin;
 
487
            yStop = _lineBuffer->scanLineMax + 1;
 
488
            dy = 1;
 
489
        }
 
490
        else
 
491
        {
 
492
            yStart = _lineBuffer->scanLineMax;
 
493
            yStop = _lineBuffer->scanLineMin - 1;
 
494
            dy = -1;
 
495
        }
 
496
 
 
497
    int y;
 
498
 
 
499
        for (y = yStart; y != yStop; y += dy)
 
500
        {
 
501
            //
 
502
            // Gather one scan line's worth of pixel data and store
 
503
            // them in _ofd->lineBuffer.
 
504
            //
 
505
 
 
506
            char *writePtr = _lineBuffer->buffer +
 
507
                             _ofd->offsetInLineBuffer[y - _ofd->minY];
 
508
            //
 
509
            // Iterate over all image channels.
 
510
            //
 
511
 
 
512
            for (unsigned int i = 0; i < _ofd->slices.size(); ++i)
 
513
            {
 
514
                //
 
515
                // Test if scan line y of this channel contains any data
 
516
        // (the scan line contains data only if y % ySampling == 0).
 
517
                //
 
518
 
 
519
                const OutSliceInfo &slice = _ofd->slices[i];
 
520
 
 
521
                if (modp (y, slice.ySampling) != 0)
 
522
                    continue;
 
523
 
 
524
                //
 
525
                // Find the x coordinates of the leftmost and rightmost
 
526
                // sampled pixels (i.e. pixels within the data window
 
527
                // for which x % xSampling == 0).
 
528
                //
 
529
 
 
530
                int dMinX = divp (_ofd->minX, slice.xSampling);
 
531
                int dMaxX = divp (_ofd->maxX, slice.xSampling);
 
532
 
 
533
                //
 
534
        // Fill the line buffer with with pixel data.
 
535
                //
 
536
 
 
537
                if (slice.zero)
 
538
                {
 
539
                    //
 
540
                    // The frame buffer contains no data for this channel.
 
541
                    // Store zeroes in _lineBuffer->buffer.
 
542
                    //
 
543
 
 
544
                    fillChannelWithZeroes (writePtr, _ofd->format, slice.type,
 
545
                                           dMaxX - dMinX + 1);
 
546
                }
 
547
                else
 
548
                {
 
549
                    //
 
550
                    // If necessary, convert the pixel data to Xdr format.
 
551
            // Then store the pixel data in _ofd->lineBuffer.
 
552
                    //
 
553
 
 
554
                    const char *linePtr = slice.base +
 
555
                                          divp (y, slice.ySampling) *
 
556
                                          slice.yStride;
 
557
 
 
558
                    const char *readPtr = linePtr + dMinX * slice.xStride;
 
559
                    const char *endPtr  = linePtr + dMaxX * slice.xStride;
 
560
 
 
561
                    copyFromFrameBuffer (writePtr, readPtr, endPtr,
 
562
                                         slice.xStride, _ofd->format,
 
563
                                         slice.type);
 
564
                }
 
565
            }
 
566
 
 
567
            if (_lineBuffer->endOfLineBufferData < writePtr)
 
568
                _lineBuffer->endOfLineBufferData = writePtr;
 
569
 
 
570
            #ifdef DEBUG
 
571
 
 
572
                assert (writePtr - (_lineBuffer->buffer +
 
573
                        _ofd->offsetInLineBuffer[y - _ofd->minY]) ==
 
574
                        (int) _ofd->bytesPerLine[y - _ofd->minY]);
 
575
 
 
576
            #endif
 
577
 
 
578
        }
 
579
 
 
580
        //
 
581
        // If the next scanline isn't past the bounds of the lineBuffer
 
582
        // then we are done, otherwise compress the linebuffer
 
583
        //
 
584
 
 
585
        if (y >= _lineBuffer->minY && y <= _lineBuffer->maxY)
 
586
            return;
 
587
 
 
588
        _lineBuffer->dataPtr = _lineBuffer->buffer;
 
589
 
 
590
        _lineBuffer->dataSize = _lineBuffer->endOfLineBufferData -
 
591
                                _lineBuffer->buffer;
 
592
 
 
593
    //
 
594
        // Compress the data
 
595
    //
 
596
 
 
597
        Compressor *compressor = _lineBuffer->compressor;
 
598
 
 
599
        if (compressor)
 
600
        {
 
601
            const char *compPtr;
 
602
 
 
603
            int compSize = compressor->compress (_lineBuffer->dataPtr,
 
604
                                                 _lineBuffer->dataSize,
 
605
                                                 _lineBuffer->minY, compPtr);
 
606
 
 
607
            if (compSize < _lineBuffer->dataSize)
 
608
            {
 
609
                _lineBuffer->dataSize = compSize;
 
610
                _lineBuffer->dataPtr = compPtr;
 
611
            }
 
612
            else if (_ofd->format == Compressor::NATIVE)
 
613
            {
 
614
                //
 
615
                // The data did not shrink during compression, but
 
616
                // we cannot write to the file using the machine's
 
617
                // native format, so we need to convert the lineBuffer
 
618
                // to Xdr.
 
619
                //
 
620
 
 
621
                convertToXdr (_ofd, _lineBuffer->buffer, _lineBuffer->minY,
 
622
                              _lineBuffer->maxY, _lineBuffer->dataSize);
 
623
            }
 
624
        }
 
625
 
 
626
        _lineBuffer->partiallyFull = false;
 
627
    }
 
628
    catch (std::exception &e)
 
629
    {
 
630
        if (!_lineBuffer->hasException)
 
631
        {
 
632
            _lineBuffer->exception = e.what ();
 
633
            _lineBuffer->hasException = true;
 
634
        }
 
635
    }
 
636
    catch (...)
 
637
    {
 
638
        if (!_lineBuffer->hasException)
 
639
        {
 
640
            _lineBuffer->exception = "unrecognized exception";
 
641
            _lineBuffer->hasException = true;
 
642
        }
 
643
    }
 
644
}
 
645
 
 
646
} // namespace
 
647
 
 
648
 
 
649
OutputFile::OutputFile
 
650
    (const char fileName[],
 
651
     const Header &header,
 
652
     int numThreads)
 
653
:
 
654
    _data (new Data (true, numThreads))
 
655
{
 
656
    try
 
657
    {
 
658
    header.sanityCheck();
 
659
    _data->os = new StdOFStream (fileName);
 
660
    initialize (header);
 
661
    }
 
662
    catch (Iex::BaseExc &e)
 
663
    {
 
664
    delete _data;
 
665
 
 
666
    REPLACE_EXC (e, "Cannot open image file "
 
667
            "\"" << fileName << "\". " << e);
 
668
    throw;
 
669
    }
 
670
    catch (...)
 
671
    {
 
672
    delete _data;
 
673
        throw;
 
674
    }
 
675
}
 
676
 
 
677
 
 
678
OutputFile::OutputFile
 
679
    (OStream &os,
 
680
     const Header &header,
 
681
     int numThreads)
 
682
:
 
683
    _data (new Data (false, numThreads))
 
684
{
 
685
    try
 
686
    {
 
687
    header.sanityCheck();
 
688
    _data->os = &os;
 
689
    initialize (header);
 
690
    }
 
691
    catch (Iex::BaseExc &e)
 
692
    {
 
693
    delete _data;
 
694
 
 
695
    REPLACE_EXC (e, "Cannot open image file "
 
696
            "\"" << os.fileName() << "\". " << e);
 
697
    throw;
 
698
    }
 
699
    catch (...)
 
700
    {
 
701
    delete _data;
 
702
        throw;
 
703
    }
 
704
}
 
705
 
 
706
 
 
707
void
 
708
OutputFile::initialize (const Header &header)
 
709
{
 
710
    _data->header = header;
 
711
 
 
712
    const Box2i &dataWindow = header.dataWindow();
 
713
 
 
714
    _data->currentScanLine = (header.lineOrder() == INCREASING_Y)?
 
715
                 dataWindow.min.y: dataWindow.max.y;
 
716
 
 
717
    _data->missingScanLines = dataWindow.max.y - dataWindow.min.y + 1;
 
718
    _data->lineOrder = header.lineOrder();
 
719
    _data->minX = dataWindow.min.x;
 
720
    _data->maxX = dataWindow.max.x;
 
721
    _data->minY = dataWindow.min.y;
 
722
    _data->maxY = dataWindow.max.y;
 
723
 
 
724
    size_t maxBytesPerLine = bytesPerLineTable (_data->header,
 
725
                        _data->bytesPerLine);
 
726
 
 
727
    for (size_t i = 0; i < _data->lineBuffers.size(); ++i)
 
728
    {
 
729
        _data->lineBuffers[i] =
 
730
        new LineBuffer (newCompressor (_data->header.compression(),
 
731
                       maxBytesPerLine,
 
732
                       _data->header));
 
733
    }
 
734
 
 
735
    LineBuffer *lineBuffer = _data->lineBuffers[0];
 
736
    _data->format = defaultFormat (lineBuffer->compressor);
 
737
    _data->linesInBuffer = numLinesInBuffer (lineBuffer->compressor);
 
738
    _data->lineBufferSize = maxBytesPerLine * _data->linesInBuffer;
 
739
 
 
740
    for (size_t i = 0; i < _data->lineBuffers.size(); i++)
 
741
        _data->lineBuffers[i]->buffer.resizeErase(_data->lineBufferSize);
 
742
 
 
743
    int lineOffsetSize = (dataWindow.max.y - dataWindow.min.y +
 
744
              _data->linesInBuffer) / _data->linesInBuffer;
 
745
 
 
746
    _data->lineOffsets.resize (lineOffsetSize);
 
747
 
 
748
    offsetInLineBufferTable (_data->bytesPerLine,
 
749
                 _data->linesInBuffer,
 
750
                 _data->offsetInLineBuffer);
 
751
 
 
752
    _data->previewPosition =
 
753
    _data->header.writeTo (*_data->os);
 
754
 
 
755
    _data->lineOffsetsPosition =
 
756
    writeLineOffsets (*_data->os, _data->lineOffsets);
 
757
 
 
758
    _data->currentPosition = _data->os->tellp();
 
759
}
 
760
 
 
761
 
 
762
OutputFile::~OutputFile ()
 
763
{
 
764
    if (_data)
 
765
    {
 
766
        {
 
767
            if (_data->lineOffsetsPosition > 0)
 
768
            {
 
769
                try
 
770
                {
 
771
                    _data->os->seekp (_data->lineOffsetsPosition);
 
772
                    writeLineOffsets (*_data->os, _data->lineOffsets);
 
773
                }
 
774
                catch (...)
 
775
                {
 
776
                    //
 
777
                    // We cannot safely throw any exceptions from here.
 
778
                    // This destructor may have been called because the
 
779
                    // stack is currently being unwound for another
 
780
                    // exception.
 
781
                    //
 
782
                }
 
783
            }
 
784
        }
 
785
 
 
786
    delete _data;
 
787
    }
 
788
}
 
789
 
 
790
 
 
791
const char *
 
792
OutputFile::fileName () const
 
793
{
 
794
    return _data->os->fileName();
 
795
}
 
796
 
 
797
 
 
798
const Header &
 
799
OutputFile::header () const
 
800
{
 
801
    return _data->header;
 
802
}
 
803
 
 
804
 
 
805
void
 
806
OutputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
 
807
{
 
808
    Lock lock (*_data);
 
809
 
 
810
    //
 
811
    // Check if the new frame buffer descriptor
 
812
    // is compatible with the image file header.
 
813
    //
 
814
 
 
815
    const ChannelList &channels = _data->header.channels();
 
816
 
 
817
    for (ChannelList::ConstIterator i = channels.begin();
 
818
     i != channels.end();
 
819
     ++i)
 
820
    {
 
821
    FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
 
822
 
 
823
    if (j == frameBuffer.end())
 
824
        continue;
 
825
 
 
826
    if (i.channel().type != j.slice().type)
 
827
    {
 
828
        THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel "
 
829
                    "of output file \"" << fileName() << "\" is "
 
830
                    "not compatible with the frame buffer's "
 
831
                    "pixel type.");
 
832
    }
 
833
 
 
834
    if (i.channel().xSampling != j.slice().xSampling ||
 
835
        i.channel().ySampling != j.slice().ySampling)
 
836
    {
 
837
        THROW (Iex::ArgExc, "X and/or y subsampling factors "
 
838
                "of \"" << i.name() << "\" channel "
 
839
                "of output file \"" << fileName() << "\" are "
 
840
                "not compatible with the frame buffer's "
 
841
                "subsampling factors.");
 
842
    }
 
843
    }
 
844
 
 
845
    //
 
846
    // Initialize slice table for writePixels().
 
847
    //
 
848
 
 
849
    vector<OutSliceInfo> slices;
 
850
 
 
851
    for (ChannelList::ConstIterator i = channels.begin();
 
852
     i != channels.end();
 
853
     ++i)
 
854
    {
 
855
    FrameBuffer::ConstIterator j = frameBuffer.find (i.name());
 
856
 
 
857
    if (j == frameBuffer.end())
 
858
    {
 
859
        //
 
860
        // Channel i is not present in the frame buffer.
 
861
        // In the file, channel i will contain only zeroes.
 
862
        //
 
863
 
 
864
        slices.push_back (OutSliceInfo (i.channel().type,
 
865
                        0, // base
 
866
                        0, // xStride,
 
867
                        0, // yStride,
 
868
                        i.channel().xSampling,
 
869
                        i.channel().ySampling,
 
870
                        true)); // zero
 
871
    }
 
872
    else
 
873
    {
 
874
        //
 
875
        // Channel i is present in the frame buffer.
 
876
        //
 
877
 
 
878
        slices.push_back (OutSliceInfo (j.slice().type,
 
879
                        j.slice().base,
 
880
                        j.slice().xStride,
 
881
                        j.slice().yStride,
 
882
                        j.slice().xSampling,
 
883
                        j.slice().ySampling,
 
884
                        false)); // zero
 
885
    }
 
886
    }
 
887
 
 
888
    //
 
889
    // Store the new frame buffer.
 
890
    //
 
891
 
 
892
    _data->frameBuffer = frameBuffer;
 
893
    _data->slices = slices;
 
894
}
 
895
 
 
896
 
 
897
const FrameBuffer &
 
898
OutputFile::frameBuffer () const
 
899
{
 
900
    Lock lock (*_data);
 
901
    return _data->frameBuffer;
 
902
}
 
903
 
 
904
 
 
905
void
 
906
OutputFile::writePixels (int numScanLines)
 
907
{
 
908
    try
 
909
    {
 
910
        Lock lock (*_data);
 
911
 
 
912
    if (_data->slices.size() == 0)
 
913
        throw Iex::ArgExc ("No frame buffer specified "
 
914
                   "as pixel data source.");
 
915
 
 
916
        //
 
917
        // Maintain two iterators:
 
918
        //     nextWriteBuffer: next linebuffer to be written to the file
 
919
        //     nextCompressBuffer: next linebuffer to compress
 
920
        //
 
921
 
 
922
        int first = (_data->currentScanLine - _data->minY) /
 
923
                         _data->linesInBuffer;
 
924
 
 
925
        int nextWriteBuffer = first;
 
926
        int nextCompressBuffer;
 
927
        int stop;
 
928
        int step;
 
929
        int scanLineMin;
 
930
        int scanLineMax;
 
931
 
 
932
        {
 
933
            //
 
934
            // Create a task group for all line buffer tasks. When the
 
935
            // taskgroup goes out of scope, the destructor waits until
 
936
        // all tasks are complete.
 
937
            //
 
938
 
 
939
            TaskGroup taskGroup;
 
940
 
 
941
            //
 
942
            // Determine the range of lineBuffers that intersect the scan
 
943
        // line range.  Then add the initial compression tasks to the
 
944
        // thread pool.  We always add in at least one task but the
 
945
        // individual task might not do anything if numScanLines == 0.
 
946
            //
 
947
 
 
948
            if (_data->lineOrder == INCREASING_Y)
 
949
            {
 
950
                int last = (_data->currentScanLine + (numScanLines - 1) -
 
951
                            _data->minY) / _data->linesInBuffer;
 
952
 
 
953
                scanLineMin = _data->currentScanLine;
 
954
                scanLineMax = _data->currentScanLine + numScanLines - 1;
 
955
 
 
956
                int numTasks = max (min ((int)_data->lineBuffers.size(),
 
957
                                         last - first + 1),
 
958
                    1);
 
959
 
 
960
                for (int i = 0; i < numTasks; i++)
 
961
        {
 
962
                    ThreadPool::addGlobalTask
 
963
                        (new LineBufferTask (&taskGroup, _data, first + i,
 
964
                                             scanLineMin, scanLineMax));
 
965
        }
 
966
 
 
967
                nextCompressBuffer = first + numTasks;
 
968
                stop = last + 1;
 
969
                step = 1;
 
970
            }
 
971
            else
 
972
            {
 
973
                int last = (_data->currentScanLine - (numScanLines - 1) -
 
974
                            _data->minY) / _data->linesInBuffer;
 
975
 
 
976
                scanLineMax = _data->currentScanLine;
 
977
                scanLineMin = _data->currentScanLine - numScanLines + 1;
 
978
 
 
979
                int numTasks = max (min ((int)_data->lineBuffers.size(),
 
980
                                         first - last + 1),
 
981
                    1);
 
982
 
 
983
                for (int i = 0; i < numTasks; i++)
 
984
        {
 
985
                    ThreadPool::addGlobalTask
 
986
                        (new LineBufferTask (&taskGroup, _data, first - i,
 
987
                                             scanLineMin, scanLineMax));
 
988
        }
 
989
 
 
990
                nextCompressBuffer = first - numTasks;
 
991
                stop = last - 1;
 
992
                step = -1;
 
993
            }
 
994
 
 
995
            while (true)
 
996
            {
 
997
                if (_data->missingScanLines <= 0)
 
998
                {
 
999
                    throw Iex::ArgExc ("Tried to write more scan lines "
 
1000
                                       "than specified by the data window.");
 
1001
                }
 
1002
 
 
1003
        //
 
1004
                // Wait until the next line buffer is ready to be written
 
1005
        //
 
1006
 
 
1007
                LineBuffer *writeBuffer =
 
1008
            _data->getLineBuffer (nextWriteBuffer);
 
1009
 
 
1010
                writeBuffer->wait();
 
1011
 
 
1012
                int numLines = writeBuffer->scanLineMax -
 
1013
                               writeBuffer->scanLineMin + 1;
 
1014
 
 
1015
                _data->missingScanLines -= numLines;
 
1016
 
 
1017
        //
 
1018
                // If the line buffer is only partially full, then it is
 
1019
        // not complete and we cannot write it to disk yet.
 
1020
        //
 
1021
 
 
1022
                if (writeBuffer->partiallyFull)
 
1023
                {
 
1024
                    _data->currentScanLine = _data->currentScanLine +
 
1025
                                             step * numLines;
 
1026
                    writeBuffer->post();
 
1027
 
 
1028
                    return;
 
1029
                }
 
1030
 
 
1031
        //
 
1032
                // Write the line buffer
 
1033
        //
 
1034
 
 
1035
                writePixelData (_data, writeBuffer);
 
1036
                nextWriteBuffer += step;
 
1037
 
 
1038
                _data->currentScanLine = _data->currentScanLine +
 
1039
                                         step * numLines;
 
1040
 
 
1041
                #ifdef DEBUG
 
1042
 
 
1043
                    assert (_data->currentScanLine ==
 
1044
                            ((_data->lineOrder == INCREASING_Y) ?
 
1045
                             writeBuffer->scanLineMax + 1:
 
1046
                             writeBuffer->scanLineMin - 1));
 
1047
 
 
1048
                #endif
 
1049
 
 
1050
        //
 
1051
                // Release the lock on the line buffer
 
1052
        //
 
1053
 
 
1054
                writeBuffer->post();
 
1055
 
 
1056
        //
 
1057
                // If this was the last line buffer in the scanline range
 
1058
        //
 
1059
 
 
1060
                if (nextWriteBuffer == stop)
 
1061
                    break;
 
1062
 
 
1063
        //
 
1064
                // If there are no more line buffers to compress,
 
1065
                // then only continue to write out remaining lineBuffers
 
1066
        //
 
1067
 
 
1068
                if (nextCompressBuffer == stop)
 
1069
                    continue;
 
1070
 
 
1071
        //
 
1072
                // Add nextCompressBuffer as a compression task
 
1073
        //
 
1074
 
 
1075
                ThreadPool::addGlobalTask
 
1076
                    (new LineBufferTask (&taskGroup, _data, nextCompressBuffer,
 
1077
                                         scanLineMin, scanLineMax));
 
1078
 
 
1079
        //
 
1080
                // Update the next line buffer we need to compress
 
1081
        //
 
1082
 
 
1083
                nextCompressBuffer += step;
 
1084
            }
 
1085
 
 
1086
        //
 
1087
            // Finish all tasks
 
1088
        //
 
1089
        }
 
1090
 
 
1091
    //
 
1092
    // Exeption handling:
 
1093
    //
 
1094
    // LineBufferTask::execute() may have encountered exceptions, but
 
1095
    // those exceptions occurred in another thread, not in the thread
 
1096
    // that is executing this call to OutputFile::writePixels().
 
1097
    // LineBufferTask::execute() has caught all exceptions and stored
 
1098
    // the exceptions' what() strings in the line buffers.
 
1099
    // Now we check if any line buffer contains a stored exception; if
 
1100
    // this is the case then we re-throw the exception in this thread.
 
1101
    // (It is possible that multiple line buffers contain stored
 
1102
    // exceptions.  We re-throw the first exception we find and
 
1103
    // ignore all others.)
 
1104
    //
 
1105
 
 
1106
    const string *exception = 0;
 
1107
 
 
1108
        for (int i = 0; i < _data->lineBuffers.size(); ++i)
 
1109
    {
 
1110
            LineBuffer *lineBuffer = _data->lineBuffers[i];
 
1111
 
 
1112
        if (lineBuffer->hasException && !exception)
 
1113
        exception = &lineBuffer->exception;
 
1114
 
 
1115
        lineBuffer->hasException = false;
 
1116
    }
 
1117
 
 
1118
    if (exception)
 
1119
        throw Iex::IoExc (*exception);
 
1120
    }
 
1121
    catch (Iex::BaseExc &e)
 
1122
    {
 
1123
    REPLACE_EXC (e, "Failed to write pixel data to image "
 
1124
                "file \"" << fileName() << "\". " << e);
 
1125
    throw;
 
1126
    }
 
1127
}
 
1128
 
 
1129
 
 
1130
int
 
1131
OutputFile::currentScanLine () const
 
1132
{
 
1133
    Lock lock (*_data);
 
1134
    return _data->currentScanLine;
 
1135
}
 
1136
 
 
1137
 
 
1138
void
 
1139
OutputFile::copyPixels (InputFile &in)
 
1140
{
 
1141
    Lock lock (*_data);
 
1142
 
 
1143
    //
 
1144
    // Check if this file's and and the InputFile's
 
1145
    // headers are compatible.
 
1146
    //
 
1147
 
 
1148
    const Header &hdr = _data->header;
 
1149
    const Header &inHdr = in.header();
 
1150
 
 
1151
    if (inHdr.find("tiles") != inHdr.end())
 
1152
    THROW (Iex::ArgExc, "Cannot copy pixels from image "
 
1153
                "file \"" << in.fileName() << "\" to image "
 
1154
                "file \"" << fileName() << "\". "
 
1155
                            "The input file is tiled, but the output file is "
 
1156
                            "not. Try using TiledOutputFile::copyPixels "
 
1157
                            "instead.");
 
1158
 
 
1159
    if (!(hdr.dataWindow() == inHdr.dataWindow()))
 
1160
    THROW (Iex::ArgExc, "Cannot copy pixels from image "
 
1161
                "file \"" << in.fileName() << "\" to image "
 
1162
                "file \"" << fileName() << "\". "
 
1163
                            "The files have different data windows.");
 
1164
 
 
1165
    if (!(hdr.lineOrder() == inHdr.lineOrder()))
 
1166
    THROW (Iex::ArgExc, "Quick pixel copy from image "
 
1167
                "file \"" << in.fileName() << "\" to image "
 
1168
                "file \"" << fileName() << "\" failed. "
 
1169
                "The files have different line orders.");
 
1170
 
 
1171
    if (!(hdr.compression() == inHdr.compression()))
 
1172
    THROW (Iex::ArgExc, "Quick pixel copy from image "
 
1173
                "file \"" << in.fileName() << "\" to image "
 
1174
                "file \"" << fileName() << "\" failed. "
 
1175
                "The files use different compression methods.");
 
1176
 
 
1177
    if (!(hdr.channels() == inHdr.channels()))
 
1178
    THROW (Iex::ArgExc, "Quick pixel copy from image "
 
1179
                "file \"" << in.fileName() << "\" to image "
 
1180
                "file \"" << fileName() << "\" failed.  "
 
1181
                "The files have different channel lists.");
 
1182
 
 
1183
    //
 
1184
    // Verify that no pixel data have been written to this file yet.
 
1185
    //
 
1186
 
 
1187
    const Box2i &dataWindow = hdr.dataWindow();
 
1188
 
 
1189
    if (_data->missingScanLines != dataWindow.max.y - dataWindow.min.y + 1)
 
1190
    THROW (Iex::LogicExc, "Quick pixel copy from image "
 
1191
                  "file \"" << in.fileName() << "\" to image "
 
1192
                  "file \"" << fileName() << "\" failed. "
 
1193
                  "\"" << fileName() << "\" already contains "
 
1194
                  "pixel data.");
 
1195
 
 
1196
    //
 
1197
    // Copy the pixel data.
 
1198
    //
 
1199
 
 
1200
    while (_data->missingScanLines > 0)
 
1201
    {
 
1202
    const char *pixelData;
 
1203
    int pixelDataSize;
 
1204
 
 
1205
    in.rawPixelData (_data->currentScanLine, pixelData, pixelDataSize);
 
1206
 
 
1207
    writePixelData (_data, lineBufferMinY (_data->currentScanLine,
 
1208
                               _data->minY,
 
1209
                               _data->linesInBuffer),
 
1210
                        pixelData, pixelDataSize);
 
1211
 
 
1212
    _data->currentScanLine += (_data->lineOrder == INCREASING_Y)?
 
1213
                   _data->linesInBuffer: -_data->linesInBuffer;
 
1214
 
 
1215
    _data->missingScanLines -= _data->linesInBuffer;
 
1216
    }
 
1217
}
 
1218
 
 
1219
 
 
1220
void
 
1221
OutputFile::updatePreviewImage (const PreviewRgba newPixels[])
 
1222
{
 
1223
    Lock lock (*_data);
 
1224
 
 
1225
    if (_data->previewPosition <= 0)
 
1226
    THROW (Iex::LogicExc, "Cannot update preview image pixels. "
 
1227
                  "File \"" << fileName() << "\" does not "
 
1228
                  "contain a preview image.");
 
1229
 
 
1230
    //
 
1231
    // Store the new pixels in the header's preview image attribute.
 
1232
    //
 
1233
 
 
1234
    PreviewImageAttribute &pia =
 
1235
    _data->header.typedAttribute <PreviewImageAttribute> ("preview");
 
1236
 
 
1237
    PreviewImage &pi = pia.value();
 
1238
    PreviewRgba *pixels = pi.pixels();
 
1239
    int numPixels = pi.width() * pi.height();
 
1240
 
 
1241
    for (int i = 0; i < numPixels; ++i)
 
1242
    pixels[i] = newPixels[i];
 
1243
 
 
1244
    //
 
1245
    // Save the current file position, jump to the position in
 
1246
    // the file where the preview image starts, store the new
 
1247
    // preview image, and jump back to the saved file position.
 
1248
    //
 
1249
 
 
1250
    Int64 savedPosition = _data->os->tellp();
 
1251
 
 
1252
    try
 
1253
    {
 
1254
    _data->os->seekp (_data->previewPosition);
 
1255
    pia.writeValueTo (*_data->os, _data->version);
 
1256
    _data->os->seekp (savedPosition);
 
1257
    }
 
1258
    catch (Iex::BaseExc &e)
 
1259
    {
 
1260
    REPLACE_EXC (e, "Cannot update preview image pixels for "
 
1261
            "file \"" << fileName() << "\". " << e);
 
1262
    throw;
 
1263
    }
 
1264
}
 
1265
 
 
1266
 
 
1267
void
 
1268
OutputFile::breakScanLine  (int y, int offset, int length, char c)
 
1269
{
 
1270
    Lock lock (*_data);
 
1271
 
 
1272
    Int64 position =
 
1273
    _data->lineOffsets[(y - _data->minY) / _data->linesInBuffer];
 
1274
 
 
1275
    if (!position)
 
1276
    THROW (Iex::ArgExc, "Cannot overwrite scan line " << y << ". "
 
1277
                "The scan line has not yet been stored in "
 
1278
                "file \"" << fileName() << "\".");
 
1279
 
 
1280
    _data->currentPosition = 0;
 
1281
    _data->os->seekp (position + offset);
 
1282
 
 
1283
    for (int i = 0; i < length; ++i)
 
1284
    _data->os->write (&c, 1);
 
1285
}
 
1286
 
 
1287
 
 
1288
} // namespace Imf