~cosme/ubuntu/precise/freeimage/freeimage-3.15.1

« back to all changes in this revision

Viewing changes to Source/OpenEXR/IlmImf/ImfOutputFile.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Andres Mejia
  • Date: 2008-05-15 03:18:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080515031800-rhaod8dwr4trqewm
Tags: 3.10.0-1
* New upstream release. Closes: #471242
* Added extra freeimage documentation in orig tarball.
* Added get-orig-source target.
* Added Homepage field in control file.
* Removing some unnecessary stuff from rules file.
* Adding some necessary build dependencies.
* Adding some modifications to allow for configuring various compiler flags.
* Fix FTBFS on amd64.
* Adding debug package.
* Added DM-Upload-Allowed: yes field.
* Added Vcs entries.

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