Coverage Report

Created: 2024-09-11 06:37

/src/freeimage-svn/FreeImage/trunk/Source/OpenEXR/IlmImf/ImfRgbaFile.cpp
Line
Count
Source (jump to first uncovered line)
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4
// Digital Ltd. LLC
5
// 
6
// All rights reserved.
7
// 
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
10
// met:
11
// *       Redistributions of source code must retain the above copyright
12
// notice, this list of conditions and the following disclaimer.
13
// *       Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following disclaimer
15
// in the documentation and/or other materials provided with the
16
// distribution.
17
// *       Neither the name of Industrial Light & Magic nor the names of
18
// its contributors may be used to endorse or promote products derived
19
// from this software without specific prior written permission. 
20
// 
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
//
33
///////////////////////////////////////////////////////////////////////////
34
35
//-----------------------------------------------------------------------------
36
//
37
//  class RgbaOutputFile
38
//  class RgbaInputFile
39
//
40
//-----------------------------------------------------------------------------
41
42
#include <ImfRgbaFile.h>
43
#include <ImfOutputFile.h>
44
#include <ImfInputFile.h>
45
#include <ImfChannelList.h>
46
#include <ImfRgbaYca.h>
47
#include <ImfStandardAttributes.h>
48
#include <ImathFun.h>
49
#include <IlmThreadMutex.h>
50
#include <Iex.h>
51
#include <string.h>
52
#include <algorithm>
53
54
#include "ImfNamespace.h"
55
56
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
57
58
using namespace std;
59
using namespace IMATH_NAMESPACE;
60
using namespace RgbaYca;
61
using namespace ILMTHREAD_NAMESPACE;
62
63
namespace {
64
65
void
66
insertChannels (Header &header, RgbaChannels rgbaChannels)
67
0
{
68
0
    ChannelList ch;
69
70
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
71
0
    {
72
0
  if (rgbaChannels & WRITE_Y)
73
0
  {
74
0
      ch.insert ("Y", Channel (HALF, 1, 1));
75
0
  }
76
77
0
  if (rgbaChannels & WRITE_C)
78
0
  {
79
0
      ch.insert ("RY", Channel (HALF, 2, 2, true));
80
0
      ch.insert ("BY", Channel (HALF, 2, 2, true));
81
0
  }
82
0
    }
83
0
    else
84
0
    {
85
0
  if (rgbaChannels & WRITE_R)
86
0
      ch.insert ("R", Channel (HALF, 1, 1));
87
88
0
  if (rgbaChannels & WRITE_G)
89
0
      ch.insert ("G", Channel (HALF, 1, 1));
90
91
0
  if (rgbaChannels & WRITE_B)
92
0
      ch.insert ("B", Channel (HALF, 1, 1));
93
0
    }
94
95
0
    if (rgbaChannels & WRITE_A)
96
0
  ch.insert ("A", Channel (HALF, 1, 1));
97
98
0
    header.channels() = ch;
99
0
}
100
101
102
RgbaChannels
103
rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "")
104
0
{
105
0
    int i = 0;
106
107
0
    if (ch.findChannel (channelNamePrefix + "R"))
108
0
  i |= WRITE_R;
109
110
0
    if (ch.findChannel (channelNamePrefix + "G"))
111
0
  i |= WRITE_G;
112
    
113
0
    if (ch.findChannel (channelNamePrefix + "B"))
114
0
  i |= WRITE_B;
115
116
0
    if (ch.findChannel (channelNamePrefix + "A"))
117
0
  i |= WRITE_A;
118
119
0
    if (ch.findChannel (channelNamePrefix + "Y"))
120
0
  i |= WRITE_Y;
121
122
0
    if (ch.findChannel (channelNamePrefix + "RY") ||
123
0
  ch.findChannel (channelNamePrefix + "BY"))
124
0
  i |= WRITE_C;
125
126
0
    return RgbaChannels (i);
127
0
}
128
129
130
string
131
prefixFromLayerName (const string &layerName, const Header &header)
132
0
{
133
0
    if (layerName.empty())
134
0
  return "";
135
136
0
    if (hasMultiView (header) && multiView(header)[0] == layerName)
137
0
  return "";
138
139
0
    return layerName + ".";
140
0
}
141
142
143
V3f
144
ywFromHeader (const Header &header)
145
0
{
146
0
    Chromaticities cr;
147
148
0
    if (hasChromaticities (header))
149
0
  cr = chromaticities (header);
150
151
0
    return computeYw (cr);
152
0
}
153
154
155
ptrdiff_t
156
cachePadding (ptrdiff_t size)
157
0
{
158
    //
159
    // Some of the buffers that are allocated by classes ToYca and
160
    // FromYca, below, may need to be padded to avoid cache thrashing.
161
    // If the difference between the buffer size and the nearest power
162
    // of two is less than CACHE_LINE_SIZE, then we add an appropriate
163
    // amount of padding.
164
    //
165
    // CACHE_LINE_SIZE must be a power of two, and it must be at
166
    // least as big as the true size of a cache line on the machine
167
    // we are running on.  (It is ok if CACHE_LINE_SIZE is larger
168
    // than a real cache line.)
169
    //
170
171
0
    static int LOG2_CACHE_LINE_SIZE = 8;
172
0
    static const ptrdiff_t CACHE_LINE_SIZE = (1 << LOG2_CACHE_LINE_SIZE);
173
174
0
    int i = LOG2_CACHE_LINE_SIZE + 2;
175
176
0
    while ((size >> i) > 1)
177
0
  ++i;
178
179
0
    if (size > (1 << (i + 1)) - 64)
180
0
  return 64 + ((1 << (i + 1)) - size);
181
182
0
    if (size < (1 << i) + 64)
183
0
  return 64 + ((1 << i) - size);
184
185
0
    return 0;
186
0
}
187
188
} // namespace
189
190
191
class RgbaOutputFile::ToYca: public Mutex
192
{
193
  public:
194
195
     ToYca (OutputFile &outputFile, RgbaChannels rgbaChannels);
196
    ~ToYca ();
197
198
    void    setYCRounding (unsigned int roundY,
199
                   unsigned int roundC);
200
201
    void    setFrameBuffer (const Rgba *base,
202
          size_t xStride,
203
          size_t yStride);
204
205
    void    writePixels (int numScanLines);
206
    int     currentScanLine () const;
207
208
  private:
209
210
    void    padTmpBuf ();
211
    void    rotateBuffers ();
212
    void    duplicateLastBuffer ();
213
    void    duplicateSecondToLastBuffer ();
214
    void    decimateChromaVertAndWriteScanLine ();
215
216
    OutputFile &  _outputFile;
217
    bool    _writeY;
218
    bool    _writeC;
219
    bool    _writeA;
220
    int     _xMin;
221
    int     _width;
222
    int     _height;
223
    int     _linesConverted;
224
    LineOrder   _lineOrder;
225
    int     _currentScanLine;
226
    V3f     _yw;
227
    Rgba *    _bufBase;
228
    Rgba *    _buf[N];
229
    Rgba *    _tmpBuf;
230
    const Rgba *  _fbBase;
231
    size_t    _fbXStride;
232
    size_t    _fbYStride;
233
    int     _roundY;
234
    int     _roundC;
235
};
236
237
238
RgbaOutputFile::ToYca::ToYca (OutputFile &outputFile,
239
            RgbaChannels rgbaChannels)
240
:
241
    _outputFile (outputFile)
242
0
{
243
0
    _writeY = (rgbaChannels & WRITE_Y)? true: false;
244
0
    _writeC = (rgbaChannels & WRITE_C)? true: false;
245
0
    _writeA = (rgbaChannels & WRITE_A)? true: false;
246
247
0
    const Box2i dw = _outputFile.header().dataWindow();
248
249
0
    _xMin = dw.min.x;
250
0
    _width  = dw.max.x - dw.min.x + 1;
251
0
    _height = dw.max.y - dw.min.y + 1;
252
253
0
    _linesConverted = 0;
254
0
    _lineOrder = _outputFile.header().lineOrder();
255
    
256
0
    if (_lineOrder == INCREASING_Y)
257
0
  _currentScanLine = dw.min.y;
258
0
    else
259
0
  _currentScanLine = dw.max.y;
260
261
0
    _yw = ywFromHeader (_outputFile.header());
262
263
0
    ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
264
265
0
    _bufBase = new Rgba[(_width + pad) * N];
266
267
0
    for (int i = 0; i < N; ++i)
268
0
  _buf[i] = _bufBase + (i * (_width + pad));
269
270
0
    _tmpBuf = new Rgba[_width + N - 1];
271
272
0
    _fbBase = 0;
273
0
    _fbXStride = 0;
274
0
    _fbYStride = 0;
275
276
0
    _roundY = 7;
277
0
    _roundC = 5;
278
0
}
279
280
281
RgbaOutputFile::ToYca::~ToYca ()
282
0
{
283
0
    delete [] _bufBase;
284
0
    delete [] _tmpBuf;
285
0
}
286
287
288
void
289
RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY,
290
              unsigned int roundC)
291
0
{
292
0
    _roundY = roundY;
293
0
    _roundC = roundC;
294
0
}
295
296
297
void
298
RgbaOutputFile::ToYca::setFrameBuffer (const Rgba *base,
299
               size_t xStride,
300
               size_t yStride)
301
0
{
302
0
    if (_fbBase == 0)
303
0
    {
304
0
  FrameBuffer fb;
305
306
0
  if (_writeY)
307
0
  {
308
0
      fb.insert ("Y",
309
0
           Slice (HALF,       // type
310
0
            (char *) &_tmpBuf[-_xMin].g,  // base
311
0
            sizeof (Rgba),      // xStride
312
0
            0,        // yStride
313
0
            1,        // xSampling
314
0
            1));        // ySampling
315
0
  }
316
317
0
  if (_writeC)
318
0
  {
319
0
      fb.insert ("RY",
320
0
           Slice (HALF,       // type
321
0
            (char *) &_tmpBuf[-_xMin].r,  // base
322
0
            sizeof (Rgba) * 2,    // xStride
323
0
            0,        // yStride
324
0
            2,        // xSampling
325
0
            2));        // ySampling
326
327
0
      fb.insert ("BY",
328
0
           Slice (HALF,       // type
329
0
            (char *) &_tmpBuf[-_xMin].b,  // base
330
0
            sizeof (Rgba) * 2,    // xStride
331
0
            0,        // yStride
332
0
            2,        // xSampling
333
0
            2));        // ySampling
334
0
  }
335
336
0
  if (_writeA)
337
0
  {
338
0
      fb.insert ("A",
339
0
           Slice (HALF,       // type
340
0
            (char *) &_tmpBuf[-_xMin].a,  // base
341
0
            sizeof (Rgba),      // xStride
342
0
            0,        // yStride
343
0
            1,        // xSampling
344
0
            1));        // ySampling
345
0
  }
346
347
0
  _outputFile.setFrameBuffer (fb);
348
0
    }
349
350
0
    _fbBase = base;
351
0
    _fbXStride = xStride;
352
0
    _fbYStride = yStride;
353
0
}
354
355
356
void
357
RgbaOutputFile::ToYca::writePixels (int numScanLines)
358
0
{
359
0
    if (_fbBase == 0)
360
0
    {
361
0
  THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the "
362
0
          "pixel data source for image file "
363
0
          "\"" << _outputFile.fileName() << "\".");
364
0
    }
365
366
0
    if (_writeY && !_writeC)
367
0
    {
368
  //
369
  // We are writing only luminance; filtering
370
  // and subsampling are not necessary.
371
  //
372
373
0
  for (int i = 0; i < numScanLines; ++i)
374
0
  {
375
      //
376
      // Copy the next scan line from the caller's
377
      // frame buffer into _tmpBuf.
378
      //
379
380
0
      for (int j = 0; j < _width; ++j)
381
0
      {
382
0
    _tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine +
383
0
             _fbXStride * (j + _xMin)];
384
0
      }
385
386
      //
387
      // Convert the scan line from RGB to luminance/chroma,
388
      // and store the result in the output file.
389
      //
390
391
0
      RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf);
392
0
      _outputFile.writePixels (1);
393
394
0
      ++_linesConverted;
395
396
0
      if (_lineOrder == INCREASING_Y)
397
0
    ++_currentScanLine;
398
0
      else
399
0
    --_currentScanLine;
400
0
  }
401
0
    }
402
0
    else
403
0
    {
404
  //
405
  // We are writing chroma; the pixels must be filtered and subsampled.
406
  //
407
408
0
  for (int i = 0; i < numScanLines; ++i)
409
0
  {
410
      //
411
      // Copy the next scan line from the caller's
412
      // frame buffer into _tmpBuf.
413
      //
414
415
0
      for (int j = 0; j < _width; ++j)
416
0
      {
417
0
    _tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine +
418
0
            _fbXStride * (j + _xMin)];
419
0
      }
420
421
      //
422
      // Convert the scan line from RGB to luminance/chroma.
423
      //
424
425
0
      RGBAtoYCA (_yw, _width, _writeA, _tmpBuf + N2, _tmpBuf + N2);
426
427
      //
428
      // Append N2 copies of the first and last pixel to the
429
      // beginning and end of the scan line.
430
      //
431
432
0
      padTmpBuf ();
433
434
      //
435
      // Filter and subsample the scan line's chroma channels
436
      // horizontally; store the result in _buf.
437
      //
438
439
0
      rotateBuffers();
440
0
      decimateChromaHoriz (_width, _tmpBuf, _buf[N - 1]);
441
442
      //
443
      // If this is the first scan line in the image,
444
      // store N2 more copies of the scan line in _buf.
445
      //
446
447
0
      if (_linesConverted == 0)
448
0
      {
449
0
    for (int j = 0; j < N2; ++j)
450
0
        duplicateLastBuffer();
451
0
      }
452
453
0
      ++_linesConverted;
454
455
      //
456
      // If we have have converted at least N2 scan lines from
457
      // RGBA to luminance/chroma, then we can start to filter
458
      // and subsample vertically, and store pixels in the
459
      // output file.
460
      //
461
462
0
      if (_linesConverted > N2)
463
0
    decimateChromaVertAndWriteScanLine();
464
465
      //
466
      // If we have already converted the last scan line in
467
      // the image to luminance/chroma, filter, subsample and
468
      // store the remaining scan lines in _buf.
469
      //
470
471
0
      if (_linesConverted >= _height)
472
0
      {
473
0
    for (int j = 0; j < N2 - _height; ++j)
474
0
        duplicateLastBuffer();
475
476
0
    duplicateSecondToLastBuffer();
477
0
    ++_linesConverted;
478
0
    decimateChromaVertAndWriteScanLine();
479
480
0
    for (int j = 1; j < min (_height, N2); ++j)
481
0
    {
482
0
        duplicateLastBuffer();
483
0
        ++_linesConverted;
484
0
        decimateChromaVertAndWriteScanLine();
485
0
    }
486
0
      }
487
488
0
      if (_lineOrder == INCREASING_Y)
489
0
    ++_currentScanLine;
490
0
      else
491
0
    --_currentScanLine;
492
0
  }
493
0
    }
494
0
}
495
496
497
int
498
RgbaOutputFile::ToYca::currentScanLine () const
499
0
{
500
0
    return _currentScanLine;
501
0
}
502
503
504
void
505
RgbaOutputFile::ToYca::padTmpBuf ()
506
0
{
507
0
    for (int i = 0; i < N2; ++i)
508
0
    {
509
0
  _tmpBuf[i] = _tmpBuf[N2];
510
0
  _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
511
0
    }
512
0
}
513
514
515
void
516
RgbaOutputFile::ToYca::rotateBuffers ()
517
0
{
518
0
    Rgba *tmp = _buf[0];
519
520
0
    for (int i = 0; i < N - 1; ++i)
521
0
  _buf[i] = _buf[i + 1];
522
523
0
    _buf[N - 1] = tmp;
524
0
}
525
526
527
void
528
RgbaOutputFile::ToYca::duplicateLastBuffer ()
529
0
{
530
0
    rotateBuffers();
531
0
    memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba));
532
0
}
533
534
535
void
536
RgbaOutputFile::ToYca::duplicateSecondToLastBuffer ()
537
0
{
538
0
    rotateBuffers();
539
0
    memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba));
540
0
}
541
542
543
void
544
RgbaOutputFile::ToYca::decimateChromaVertAndWriteScanLine ()
545
0
{
546
0
    if (_linesConverted & 1)
547
0
  memcpy (_tmpBuf, _buf[N2], _width * sizeof (Rgba));
548
0
    else
549
0
  decimateChromaVert (_width, _buf, _tmpBuf);
550
551
0
    if (_writeY && _writeC)
552
0
  roundYCA (_width, _roundY, _roundC, _tmpBuf, _tmpBuf);
553
554
0
    _outputFile.writePixels (1);
555
0
}
556
557
558
RgbaOutputFile::RgbaOutputFile (const char name[],
559
        const Header &header,
560
        RgbaChannels rgbaChannels,
561
                                int numThreads):
562
    _outputFile (0),
563
    _toYca (0)
564
0
{
565
0
    Header hd (header);
566
0
    insertChannels (hd, rgbaChannels);
567
0
    _outputFile = new OutputFile (name, hd, numThreads);
568
569
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
570
0
  _toYca = new ToYca (*_outputFile, rgbaChannels);
571
0
}
572
573
574
RgbaOutputFile::RgbaOutputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os,
575
        const Header &header,
576
        RgbaChannels rgbaChannels,
577
                                int numThreads):
578
    _outputFile (0),
579
    _toYca (0)
580
0
{
581
0
    Header hd (header);
582
0
    insertChannels (hd, rgbaChannels);
583
0
    _outputFile = new OutputFile (os, hd, numThreads);
584
585
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
586
0
  _toYca = new ToYca (*_outputFile, rgbaChannels);
587
0
}
588
589
590
RgbaOutputFile::RgbaOutputFile (const char name[],
591
        const IMATH_NAMESPACE::Box2i &displayWindow,
592
        const IMATH_NAMESPACE::Box2i &dataWindow,
593
        RgbaChannels rgbaChannels,
594
        float pixelAspectRatio,
595
        const IMATH_NAMESPACE::V2f screenWindowCenter,
596
        float screenWindowWidth,
597
        LineOrder lineOrder,
598
        Compression compression,
599
                                int numThreads):
600
    _outputFile (0),
601
    _toYca (0)
602
0
{
603
0
    Header hd (displayWindow,
604
0
         dataWindow.isEmpty()? displayWindow: dataWindow,
605
0
         pixelAspectRatio,
606
0
         screenWindowCenter,
607
0
         screenWindowWidth,
608
0
         lineOrder,
609
0
         compression);
610
611
0
    insertChannels (hd, rgbaChannels);
612
0
    _outputFile = new OutputFile (name, hd, numThreads);
613
614
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
615
0
  _toYca = new ToYca (*_outputFile, rgbaChannels);
616
0
}
617
618
619
RgbaOutputFile::RgbaOutputFile (const char name[],
620
        int width,
621
        int height,
622
        RgbaChannels rgbaChannels,
623
        float pixelAspectRatio,
624
        const IMATH_NAMESPACE::V2f screenWindowCenter,
625
        float screenWindowWidth,
626
        LineOrder lineOrder,
627
        Compression compression,
628
                                int numThreads):
629
    _outputFile (0),
630
    _toYca (0)
631
0
{
632
0
    Header hd (width,
633
0
         height,
634
0
         pixelAspectRatio,
635
0
         screenWindowCenter,
636
0
         screenWindowWidth,
637
0
         lineOrder,
638
0
         compression);
639
640
0
    insertChannels (hd, rgbaChannels);
641
0
    _outputFile = new OutputFile (name, hd, numThreads);
642
643
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
644
0
  _toYca = new ToYca (*_outputFile, rgbaChannels);
645
0
}
646
647
648
RgbaOutputFile::~RgbaOutputFile ()
649
0
{
650
0
    delete _toYca;
651
0
    delete _outputFile;
652
0
}
653
654
655
void
656
RgbaOutputFile::setFrameBuffer (const Rgba *base,
657
        size_t xStride,
658
        size_t yStride)
659
0
{
660
0
    if (_toYca)
661
0
    {
662
0
  Lock lock (*_toYca);
663
0
  _toYca->setFrameBuffer (base, xStride, yStride);
664
0
    }
665
0
    else
666
0
    {
667
0
  size_t xs = xStride * sizeof (Rgba);
668
0
  size_t ys = yStride * sizeof (Rgba);
669
670
0
  FrameBuffer fb;
671
672
0
  fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys));
673
0
  fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys));
674
0
  fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys));
675
0
  fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys));
676
677
0
  _outputFile->setFrameBuffer (fb);
678
0
    }
679
0
}
680
681
682
void  
683
RgbaOutputFile::writePixels (int numScanLines)
684
0
{
685
0
    if (_toYca)
686
0
    {
687
0
  Lock lock (*_toYca);
688
0
  _toYca->writePixels (numScanLines);
689
0
    }
690
0
    else
691
0
    {
692
0
  _outputFile->writePixels (numScanLines);
693
0
    }
694
0
}
695
696
697
int 
698
RgbaOutputFile::currentScanLine () const
699
0
{
700
0
    if (_toYca)
701
0
    {
702
0
  Lock lock (*_toYca);
703
0
  return _toYca->currentScanLine();
704
0
    }
705
0
    else
706
0
    {
707
0
  return _outputFile->currentScanLine();
708
0
    }
709
0
}
710
711
712
const Header &
713
RgbaOutputFile::header () const
714
0
{
715
0
    return _outputFile->header();
716
0
}
717
718
719
const FrameBuffer &
720
RgbaOutputFile::frameBuffer () const
721
0
{
722
0
    return _outputFile->frameBuffer();
723
0
}
724
725
726
const IMATH_NAMESPACE::Box2i &
727
RgbaOutputFile::displayWindow () const
728
0
{
729
0
    return _outputFile->header().displayWindow();
730
0
}
731
732
733
const IMATH_NAMESPACE::Box2i &
734
RgbaOutputFile::dataWindow () const
735
0
{
736
0
    return _outputFile->header().dataWindow();
737
0
}
738
739
740
float 
741
RgbaOutputFile::pixelAspectRatio () const
742
0
{
743
0
    return _outputFile->header().pixelAspectRatio();
744
0
}
745
746
747
const IMATH_NAMESPACE::V2f
748
RgbaOutputFile::screenWindowCenter () const
749
0
{
750
0
    return _outputFile->header().screenWindowCenter();
751
0
}
752
753
754
float 
755
RgbaOutputFile::screenWindowWidth () const
756
0
{
757
0
    return _outputFile->header().screenWindowWidth();
758
0
}
759
760
761
LineOrder
762
RgbaOutputFile::lineOrder () const
763
0
{
764
0
    return _outputFile->header().lineOrder();
765
0
}
766
767
768
Compression
769
RgbaOutputFile::compression () const
770
0
{
771
0
    return _outputFile->header().compression();
772
0
}
773
774
775
RgbaChannels
776
RgbaOutputFile::channels () const
777
0
{
778
0
    return rgbaChannels (_outputFile->header().channels());
779
0
}
780
781
782
void    
783
RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
784
0
{
785
0
    _outputFile->updatePreviewImage (newPixels);
786
0
}
787
788
789
void    
790
RgbaOutputFile::setYCRounding (unsigned int roundY, unsigned int roundC)
791
0
{
792
0
    if (_toYca)
793
0
    {
794
0
  Lock lock (*_toYca);
795
0
  _toYca->setYCRounding (roundY, roundC);
796
0
    }
797
0
}
798
799
800
void  
801
RgbaOutputFile::breakScanLine  (int y, int offset, int length, char c)
802
0
{
803
0
    _outputFile->breakScanLine (y, offset, length, c);
804
0
}
805
806
807
class RgbaInputFile::FromYca: public Mutex
808
{
809
  public:
810
811
     FromYca (InputFile &inputFile, RgbaChannels rgbaChannels);
812
    ~FromYca ();
813
814
    void    setFrameBuffer (Rgba *base,
815
          size_t xStride,
816
          size_t yStride,
817
          const string &channelNamePrefix);
818
819
    void    readPixels (int scanLine1, int scanLine2);
820
821
  private:
822
823
    void    readPixels (int scanLine);
824
    void    rotateBuf1 (int d);
825
    void    rotateBuf2 (int d);
826
    void    readYCAScanLine (int y, Rgba buf[]);
827
    void    padTmpBuf ();
828
829
    InputFile &   _inputFile;
830
    bool    _readC;
831
    int     _xMin;
832
    int     _yMin;
833
    int     _yMax;
834
    int     _width;
835
    int     _height;
836
    int     _currentScanLine;
837
    LineOrder   _lineOrder;
838
    V3f     _yw;
839
    Rgba *    _bufBase;
840
    Rgba *    _buf1[N + 2];
841
    Rgba *    _buf2[3];
842
    Rgba *    _tmpBuf;
843
    Rgba *    _fbBase;
844
    size_t    _fbXStride;
845
    size_t    _fbYStride;
846
};
847
848
849
RgbaInputFile::FromYca::FromYca (InputFile &inputFile,
850
         RgbaChannels rgbaChannels)
851
:
852
    _inputFile (inputFile)
853
0
{
854
0
    _readC = (rgbaChannels & WRITE_C)? true: false;
855
856
0
    const Box2i dw = _inputFile.header().dataWindow();
857
858
0
    _xMin = dw.min.x;
859
0
    _yMin = dw.min.y;
860
0
    _yMax = dw.max.y;
861
0
    _width  = dw.max.x - dw.min.x + 1;
862
0
    _height = dw.max.y - dw.min.y + 1;
863
0
    _currentScanLine = dw.min.y - N - 2;
864
0
    _lineOrder = _inputFile.header().lineOrder();
865
0
    _yw = ywFromHeader (_inputFile.header());
866
867
0
    ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
868
869
0
    _bufBase = new Rgba[(_width + pad) * (N + 2 + 3)];
870
871
0
    for (int i = 0; i < N + 2; ++i)
872
0
  _buf1[i] = _bufBase + (i * (_width + pad));
873
    
874
0
    for (int i = 0; i < 3; ++i)
875
0
  _buf2[i] = _bufBase + ((i + N + 2) * (_width + pad));
876
877
0
    _tmpBuf = new Rgba[_width + N - 1];
878
879
0
    _fbBase = 0;
880
0
    _fbXStride = 0;
881
0
    _fbYStride = 0;
882
0
}
883
884
885
RgbaInputFile::FromYca::~FromYca ()
886
0
{
887
0
    delete [] _bufBase;
888
0
    delete [] _tmpBuf;
889
0
}
890
891
892
void
893
RgbaInputFile::FromYca::setFrameBuffer (Rgba *base,
894
          size_t xStride,
895
          size_t yStride,
896
          const string &channelNamePrefix)
897
0
{
898
0
    if (_fbBase == 0)
899
0
    {
900
0
  FrameBuffer fb;
901
902
0
  fb.insert (channelNamePrefix + "Y",
903
0
       Slice (HALF,         // type
904
0
        (char *) &_tmpBuf[N2 - _xMin].g,  // base
905
0
        sizeof (Rgba),      // xStride
906
0
        0,          // yStride
907
0
        1,          // xSampling
908
0
        1,          // ySampling
909
0
        0.5));        // fillValue
910
911
0
  if (_readC)
912
0
  {
913
0
      fb.insert (channelNamePrefix + "RY",
914
0
           Slice (HALF,       // type
915
0
            (char *) &_tmpBuf[N2 - _xMin].r,  // base
916
0
            sizeof (Rgba) * 2,    // xStride
917
0
            0,        // yStride
918
0
            2,        // xSampling
919
0
            2,        // ySampling
920
0
            0.0));        // fillValue
921
922
0
      fb.insert (channelNamePrefix + "BY",
923
0
           Slice (HALF,       // type
924
0
            (char *) &_tmpBuf[N2 - _xMin].b,  // base
925
0
            sizeof (Rgba) * 2,    // xStride
926
0
            0,        // yStride
927
0
            2,        // xSampling
928
0
            2,        // ySampling
929
0
            0.0));        // fillValue
930
0
  }
931
932
0
  fb.insert (channelNamePrefix + "A",
933
0
       Slice (HALF,         // type
934
0
        (char *) &_tmpBuf[N2 - _xMin].a,  // base
935
0
        sizeof (Rgba),      // xStride
936
0
        0,          // yStride
937
0
        1,          // xSampling
938
0
        1,          // ySampling
939
0
        1.0));        // fillValue
940
941
0
  _inputFile.setFrameBuffer (fb);
942
0
    }
943
944
0
    _fbBase = base;
945
0
    _fbXStride = xStride;
946
0
    _fbYStride = yStride;
947
0
}
948
949
950
void  
951
RgbaInputFile::FromYca::readPixels (int scanLine1, int scanLine2)
952
0
{
953
0
    int minY = min (scanLine1, scanLine2);
954
0
    int maxY = max (scanLine1, scanLine2);
955
956
0
    if (_lineOrder == INCREASING_Y)
957
0
    {
958
0
  for (int y = minY; y <= maxY; ++y)
959
0
      readPixels (y);
960
0
    }
961
0
    else
962
0
    {
963
0
  for (int y = maxY; y >= minY; --y)
964
0
      readPixels (y);
965
0
    }
966
0
}
967
968
969
void  
970
RgbaInputFile::FromYca::readPixels (int scanLine)
971
0
{
972
0
    if (_fbBase == 0)
973
0
    {
974
0
  THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the "
975
0
          "pixel data destination for image file "
976
0
          "\"" << _inputFile.fileName() << "\".");
977
0
    }
978
979
    //
980
    // In order to convert one scan line to RGB format, we need that
981
    // scan line plus N2+1 extra scan lines above and N2+1 scan lines
982
    // below in luminance/chroma format.
983
    //
984
    // We allow random access to scan lines, but we buffer partially
985
    // processed luminance/chroma data in order to make reading pixels
986
    // in increasing y or decreasing y order reasonably efficient:
987
    //
988
    //  _currentScanLine  holds the y coordinate of the scan line
989
    //        that was most recently read.
990
    //
991
    //  _buf1     contains scan lines _currentScanLine-N2-1
992
    //        through _currentScanLine+N2+1 in
993
    //        luminance/chroma format.  Odd-numbered
994
    //        lines contain no chroma data.  Even-numbered
995
    //        lines have valid chroma data for all pixels.
996
    //
997
    //  _buf2     contains scan lines _currentScanLine-1
998
    //        through _currentScanLine+1, in RGB format.
999
    //        Super-saturated pixels (see ImfRgbaYca.h)
1000
    //        have not yet been eliminated.
1001
    //
1002
    // If the scan line we are trying to read now is close enough to
1003
    // _currentScanLine, we don't have to recompute the contents of _buf1
1004
    // and _buf2 from scratch.  We can rotate _buf1 and _buf2, and fill
1005
    // in the missing data.
1006
    //
1007
1008
0
    int dy = scanLine - _currentScanLine;
1009
1010
0
    if (abs (dy) < N + 2)
1011
0
  rotateBuf1 (dy);
1012
1013
0
    if (abs (dy) < 3)
1014
0
  rotateBuf2 (dy);
1015
1016
0
    if (dy < 0)
1017
0
    {
1018
0
  {
1019
0
      int n = min (-dy, N + 2);
1020
0
      int yMin = scanLine - N2 - 1;
1021
1022
0
      for (int i = n - 1; i >= 0; --i)
1023
0
    readYCAScanLine (yMin + i, _buf1[i]);
1024
0
  }
1025
1026
0
  {
1027
0
      int n = min (-dy, 3);
1028
1029
0
      for (int i = 0; i < n; ++i)
1030
0
      {
1031
0
    if ((scanLine + i) & 1)
1032
0
    {
1033
0
        YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
1034
0
    }
1035
0
    else
1036
0
    {
1037
0
        reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
1038
0
        YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
1039
0
    }
1040
0
      }
1041
0
  }
1042
0
    }
1043
0
    else
1044
0
    {
1045
0
  {
1046
0
      int n = min (dy, N + 2);
1047
0
      int yMax = scanLine + N2 + 1;
1048
1049
0
      for (int i = n - 1; i >= 0; --i)
1050
0
    readYCAScanLine (yMax - i, _buf1[N + 1 - i]);
1051
0
  }
1052
1053
0
  {
1054
0
      int n = min (dy, 3);
1055
1056
0
      for (int i = 2; i > 2 - n; --i)
1057
0
      {
1058
0
    if ((scanLine + i) & 1)
1059
0
    {
1060
0
        YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
1061
0
    }
1062
0
    else
1063
0
    {
1064
0
        reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
1065
0
        YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
1066
0
    }
1067
0
      }
1068
0
  }
1069
0
    }
1070
1071
0
    fixSaturation (_yw, _width, _buf2, _tmpBuf);
1072
1073
0
    for (int i = 0; i < _width; ++i)
1074
0
  _fbBase[_fbYStride * scanLine + _fbXStride * (i + _xMin)] = _tmpBuf[i];
1075
1076
0
    _currentScanLine = scanLine;
1077
0
}
1078
1079
1080
void
1081
RgbaInputFile::FromYca::rotateBuf1 (int d)
1082
0
{
1083
0
    d = modp (d, N + 2);
1084
1085
0
    Rgba *tmp[N + 2];
1086
1087
0
    for (int i = 0; i < N + 2; ++i)
1088
0
  tmp[i] = _buf1[i];
1089
1090
0
    for (int i = 0; i < N + 2; ++i)
1091
0
  _buf1[i] = tmp[(i + d) % (N + 2)];
1092
0
}
1093
1094
1095
void
1096
RgbaInputFile::FromYca::rotateBuf2 (int d)
1097
0
{
1098
0
    d = modp (d, 3);
1099
1100
0
    Rgba *tmp[3];
1101
1102
0
    for (int i = 0; i < 3; ++i)
1103
0
  tmp[i] = _buf2[i];
1104
1105
0
    for (int i = 0; i < 3; ++i)
1106
0
  _buf2[i] = tmp[(i + d) % 3];
1107
0
}
1108
1109
1110
void
1111
RgbaInputFile::FromYca::readYCAScanLine (int y, Rgba *buf)
1112
0
{
1113
    //
1114
    // Clamp y.
1115
    //
1116
1117
0
    if (y < _yMin)
1118
0
  y = _yMin;
1119
0
    else if (y > _yMax)
1120
0
  y = _yMax - 1;
1121
1122
    //
1123
    // Read scan line y into _tmpBuf.
1124
    //
1125
1126
0
    _inputFile.readPixels (y);
1127
1128
    //
1129
    // Reconstruct missing chroma samples and copy
1130
    // the scan line into buf.
1131
    //
1132
1133
0
    if (!_readC)
1134
0
    {
1135
0
  for (int i = 0; i < _width; ++i)
1136
0
  {
1137
0
      _tmpBuf[i + N2].r = 0;
1138
0
      _tmpBuf[i + N2].b = 0;
1139
0
  }
1140
0
    }
1141
1142
0
    if (y & 1)
1143
0
    {
1144
0
  memcpy (buf, _tmpBuf + N2, _width * sizeof (Rgba));
1145
0
    }
1146
0
    else
1147
0
    {
1148
0
  padTmpBuf();
1149
0
  reconstructChromaHoriz (_width, _tmpBuf, buf);
1150
0
    }
1151
0
}
1152
1153
1154
void
1155
RgbaInputFile::FromYca::padTmpBuf ()
1156
0
{
1157
0
    for (int i = 0; i < N2; ++i)
1158
0
    {
1159
0
  _tmpBuf[i] = _tmpBuf[N2];
1160
0
  _tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
1161
0
    }
1162
0
}
1163
1164
1165
RgbaInputFile::RgbaInputFile (const char name[], int numThreads):
1166
    _inputFile (new InputFile (name, numThreads)),
1167
    _fromYca (0),
1168
    _channelNamePrefix ("")
1169
0
{
1170
0
    RgbaChannels rgbaChannels = channels();
1171
1172
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
1173
0
  _fromYca = new FromYca (*_inputFile, rgbaChannels);
1174
0
}
1175
1176
1177
RgbaInputFile::RgbaInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
1178
    _inputFile (new InputFile (is, numThreads)),
1179
    _fromYca (0),
1180
    _channelNamePrefix ("")
1181
0
{
1182
0
    RgbaChannels rgbaChannels = channels();
1183
1184
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
1185
0
  _fromYca = new FromYca (*_inputFile, rgbaChannels);
1186
0
}
1187
1188
1189
RgbaInputFile::RgbaInputFile (const char name[],
1190
            const string &layerName,
1191
            int numThreads)
1192
:
1193
    _inputFile (new InputFile (name, numThreads)),
1194
    _fromYca (0),
1195
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
1196
0
{
1197
0
    RgbaChannels rgbaChannels = channels();
1198
1199
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
1200
0
  _fromYca = new FromYca (*_inputFile, rgbaChannels);
1201
0
}
1202
1203
1204
RgbaInputFile::RgbaInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is,
1205
            const string &layerName,
1206
            int numThreads)
1207
:
1208
    _inputFile (new InputFile (is, numThreads)),
1209
    _fromYca (0),
1210
    _channelNamePrefix (prefixFromLayerName (layerName, _inputFile->header()))
1211
0
{
1212
0
    RgbaChannels rgbaChannels = channels();
1213
1214
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
1215
0
  _fromYca = new FromYca (*_inputFile, rgbaChannels);
1216
0
}
1217
1218
1219
RgbaInputFile::~RgbaInputFile ()
1220
0
{
1221
0
    delete _inputFile;
1222
0
    delete _fromYca;
1223
0
}
1224
1225
1226
void  
1227
RgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
1228
0
{
1229
0
    if (_fromYca)
1230
0
    {
1231
0
  Lock lock (*_fromYca);
1232
0
  _fromYca->setFrameBuffer (base, xStride, yStride, _channelNamePrefix);
1233
0
    }
1234
0
    else
1235
0
    {
1236
0
  size_t xs = xStride * sizeof (Rgba);
1237
0
  size_t ys = yStride * sizeof (Rgba);
1238
1239
0
  FrameBuffer fb;
1240
1241
0
  fb.insert (_channelNamePrefix + "R",
1242
0
       Slice (HALF,
1243
0
        (char *) &base[0].r,
1244
0
        xs, ys,
1245
0
        1, 1,   // xSampling, ySampling
1246
0
        0.0));  // fillValue
1247
1248
0
  fb.insert (_channelNamePrefix + "G",
1249
0
       Slice (HALF,
1250
0
        (char *) &base[0].g,
1251
0
        xs, ys,
1252
0
        1, 1,   // xSampling, ySampling
1253
0
        0.0));  // fillValue
1254
1255
0
  fb.insert (_channelNamePrefix + "B",
1256
0
       Slice (HALF,
1257
0
        (char *) &base[0].b,
1258
0
        xs, ys,
1259
0
        1, 1,   // xSampling, ySampling
1260
0
        0.0));  // fillValue
1261
1262
0
  fb.insert (_channelNamePrefix + "A",
1263
0
       Slice (HALF,
1264
0
        (char *) &base[0].a,
1265
0
        xs, ys,
1266
0
        1, 1,   // xSampling, ySampling
1267
0
        1.0));  // fillValue
1268
1269
0
  _inputFile->setFrameBuffer (fb);
1270
0
    }
1271
0
}
1272
1273
1274
void
1275
RgbaInputFile::setLayerName (const string &layerName)
1276
0
{
1277
0
    delete _fromYca;
1278
0
    _fromYca = 0;
1279
1280
0
    _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header());
1281
1282
0
    RgbaChannels rgbaChannels = channels();
1283
1284
0
    if (rgbaChannels & (WRITE_Y | WRITE_C))
1285
0
  _fromYca = new FromYca (*_inputFile, rgbaChannels);
1286
1287
0
    FrameBuffer fb;
1288
0
    _inputFile->setFrameBuffer (fb);
1289
0
}
1290
1291
1292
void  
1293
RgbaInputFile::readPixels (int scanLine1, int scanLine2)
1294
0
{
1295
0
    if (_fromYca)
1296
0
    {
1297
0
  Lock lock (*_fromYca);
1298
0
  _fromYca->readPixels (scanLine1, scanLine2);
1299
0
    }
1300
0
    else
1301
0
    {
1302
0
  _inputFile->readPixels (scanLine1, scanLine2);
1303
0
    }
1304
0
}
1305
1306
1307
void  
1308
RgbaInputFile::readPixels (int scanLine)
1309
0
{
1310
0
    readPixels (scanLine, scanLine);
1311
0
}
1312
1313
1314
bool
1315
RgbaInputFile::isComplete () const
1316
0
{
1317
0
    return _inputFile->isComplete();
1318
0
}
1319
1320
1321
const Header &
1322
RgbaInputFile::header () const
1323
0
{
1324
0
    return _inputFile->header();
1325
0
}
1326
1327
1328
const char *
1329
RgbaInputFile::fileName () const
1330
0
{
1331
0
    return _inputFile->fileName();
1332
0
}
1333
1334
1335
const FrameBuffer & 
1336
RgbaInputFile::frameBuffer () const
1337
0
{
1338
0
    return _inputFile->frameBuffer();
1339
0
}
1340
1341
1342
const IMATH_NAMESPACE::Box2i &
1343
RgbaInputFile::displayWindow () const
1344
0
{
1345
0
    return _inputFile->header().displayWindow();
1346
0
}
1347
1348
1349
const IMATH_NAMESPACE::Box2i &
1350
RgbaInputFile::dataWindow () const
1351
0
{
1352
0
    return _inputFile->header().dataWindow();
1353
0
}
1354
1355
1356
float 
1357
RgbaInputFile::pixelAspectRatio () const
1358
0
{
1359
0
    return _inputFile->header().pixelAspectRatio();
1360
0
}
1361
1362
1363
const IMATH_NAMESPACE::V2f  
1364
RgbaInputFile::screenWindowCenter () const
1365
0
{
1366
0
    return _inputFile->header().screenWindowCenter();
1367
0
}
1368
1369
1370
float 
1371
RgbaInputFile::screenWindowWidth () const
1372
0
{
1373
0
    return _inputFile->header().screenWindowWidth();
1374
0
}
1375
1376
1377
LineOrder
1378
RgbaInputFile::lineOrder () const
1379
0
{
1380
0
    return _inputFile->header().lineOrder();
1381
0
}
1382
1383
1384
Compression
1385
RgbaInputFile::compression () const
1386
0
{
1387
0
    return _inputFile->header().compression();
1388
0
}
1389
1390
1391
RgbaChannels  
1392
RgbaInputFile::channels () const
1393
0
{
1394
0
    return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix);
1395
0
}
1396
1397
1398
int
1399
RgbaInputFile::version () const
1400
0
{
1401
0
    return _inputFile->version();
1402
0
}
1403
1404
1405
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT