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