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