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
insertChannels(Header & header,RgbaChannels rgbaChannels)66 insertChannels (Header &header, RgbaChannels rgbaChannels)
67 {
68     ChannelList ch;
69 
70     if (rgbaChannels & (WRITE_Y | WRITE_C))
71     {
72 	if (rgbaChannels & WRITE_Y)
73 	{
74 	    ch.insert ("Y", Channel (HALF, 1, 1));
75 	}
76 
77 	if (rgbaChannels & WRITE_C)
78 	{
79 	    ch.insert ("RY", Channel (HALF, 2, 2, true));
80 	    ch.insert ("BY", Channel (HALF, 2, 2, true));
81 	}
82     }
83     else
84     {
85 	if (rgbaChannels & WRITE_R)
86 	    ch.insert ("R", Channel (HALF, 1, 1));
87 
88 	if (rgbaChannels & WRITE_G)
89 	    ch.insert ("G", Channel (HALF, 1, 1));
90 
91 	if (rgbaChannels & WRITE_B)
92 	    ch.insert ("B", Channel (HALF, 1, 1));
93     }
94 
95     if (rgbaChannels & WRITE_A)
96 	ch.insert ("A", Channel (HALF, 1, 1));
97 
98     header.channels() = ch;
99 }
100 
101 
102 RgbaChannels
rgbaChannels(const ChannelList & ch,const string & channelNamePrefix="")103 rgbaChannels (const ChannelList &ch, const string &channelNamePrefix = "")
104 {
105     int i = 0;
106 
107     if (ch.findChannel (channelNamePrefix + "R"))
108 	i |= WRITE_R;
109 
110     if (ch.findChannel (channelNamePrefix + "G"))
111 	i |= WRITE_G;
112 
113     if (ch.findChannel (channelNamePrefix + "B"))
114 	i |= WRITE_B;
115 
116     if (ch.findChannel (channelNamePrefix + "A"))
117 	i |= WRITE_A;
118 
119     if (ch.findChannel (channelNamePrefix + "Y"))
120 	i |= WRITE_Y;
121 
122     if (ch.findChannel (channelNamePrefix + "RY") ||
123 	ch.findChannel (channelNamePrefix + "BY"))
124 	i |= WRITE_C;
125 
126     return RgbaChannels (i);
127 }
128 
129 
130 string
prefixFromLayerName(const string & layerName,const Header & header)131 prefixFromLayerName (const string &layerName, const Header &header)
132 {
133     if (layerName.empty())
134 	return "";
135 
136     if (hasMultiView (header) && multiView(header)[0] == layerName)
137 	return "";
138 
139     return layerName + ".";
140 }
141 
142 
143 V3f
ywFromHeader(const Header & header)144 ywFromHeader (const Header &header)
145 {
146     Chromaticities cr;
147 
148     if (hasChromaticities (header))
149 	cr = chromaticities (header);
150 
151     return computeYw (cr);
152 }
153 
154 
155 ptrdiff_t
cachePadding(ptrdiff_t size)156 cachePadding (ptrdiff_t size)
157 {
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     static int LOG2_CACHE_LINE_SIZE = 8;
172     static const ptrdiff_t CACHE_LINE_SIZE = (1 << LOG2_CACHE_LINE_SIZE);
173 
174     int i = LOG2_CACHE_LINE_SIZE + 2;
175 
176     while ((size >> i) > 1)
177 	++i;
178 
179     if (size > (1 << (i + 1)) - 64)
180 	return 64 + ((1 << (i + 1)) - size);
181 
182     if (size < (1 << i) + 64)
183 	return 64 + ((1 << i) - size);
184 
185     return 0;
186 }
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 
ToYca(OutputFile & outputFile,RgbaChannels rgbaChannels)238 RgbaOutputFile::ToYca::ToYca (OutputFile &outputFile,
239 			      RgbaChannels rgbaChannels)
240 :
241     _outputFile (outputFile)
242 {
243     _writeY = (rgbaChannels & WRITE_Y)? true: false;
244     _writeC = (rgbaChannels & WRITE_C)? true: false;
245     _writeA = (rgbaChannels & WRITE_A)? true: false;
246 
247     const Box2i dw = _outputFile.header().dataWindow();
248 
249     _xMin = dw.min.x;
250     _width  = dw.max.x - dw.min.x + 1;
251     _height = dw.max.y - dw.min.y + 1;
252 
253     _linesConverted = 0;
254     _lineOrder = _outputFile.header().lineOrder();
255 
256     if (_lineOrder == INCREASING_Y)
257 	_currentScanLine = dw.min.y;
258     else
259 	_currentScanLine = dw.max.y;
260 
261     _yw = ywFromHeader (_outputFile.header());
262 
263     ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
264 
265     _bufBase = new Rgba[(_width + pad) * N];
266 
267     for (int i = 0; i < N; ++i)
268 	_buf[i] = _bufBase + (i * (_width + pad));
269 
270     _tmpBuf = new Rgba[_width + N - 1];
271 
272     _fbBase = 0;
273     _fbXStride = 0;
274     _fbYStride = 0;
275 
276     _roundY = 7;
277     _roundC = 5;
278 }
279 
280 
~ToYca()281 RgbaOutputFile::ToYca::~ToYca ()
282 {
283     delete [] _bufBase;
284     delete [] _tmpBuf;
285 }
286 
287 
288 void
setYCRounding(unsigned int roundY,unsigned int roundC)289 RgbaOutputFile::ToYca::setYCRounding (unsigned int roundY,
290 				      unsigned int roundC)
291 {
292     _roundY = roundY;
293     _roundC = roundC;
294 }
295 
296 
297 void
setFrameBuffer(const Rgba * base,size_t xStride,size_t yStride)298 RgbaOutputFile::ToYca::setFrameBuffer (const Rgba *base,
299 				       size_t xStride,
300 				       size_t yStride)
301 {
302     if (_fbBase == 0)
303     {
304 	FrameBuffer fb;
305 
306 	if (_writeY)
307 	{
308 	    fb.insert ("Y",
309 		       Slice (HALF,				// type
310 			      (char *) &_tmpBuf[-_xMin].g,	// base
311 			      sizeof (Rgba),			// xStride
312 			      0,				// yStride
313 			      1,				// xSampling
314 			      1));				// ySampling
315 	}
316 
317 	if (_writeC)
318 	{
319 	    fb.insert ("RY",
320 		       Slice (HALF,				// type
321 			      (char *) &_tmpBuf[-_xMin].r,	// base
322 			      sizeof (Rgba) * 2,		// xStride
323 			      0,				// yStride
324 			      2,				// xSampling
325 			      2));				// ySampling
326 
327 	    fb.insert ("BY",
328 		       Slice (HALF,				// type
329 			      (char *) &_tmpBuf[-_xMin].b,	// base
330 			      sizeof (Rgba) * 2,		// xStride
331 			      0,				// yStride
332 			      2,				// xSampling
333 			      2));				// ySampling
334 	}
335 
336 	if (_writeA)
337 	{
338 	    fb.insert ("A",
339 		       Slice (HALF,				// type
340 			      (char *) &_tmpBuf[-_xMin].a,	// base
341 			      sizeof (Rgba),			// xStride
342 			      0,				// yStride
343 			      1,				// xSampling
344 			      1));				// ySampling
345 	}
346 
347 	_outputFile.setFrameBuffer (fb);
348     }
349 
350     _fbBase = base;
351     _fbXStride = xStride;
352     _fbYStride = yStride;
353 }
354 
355 
356 void
writePixels(int numScanLines)357 RgbaOutputFile::ToYca::writePixels (int numScanLines)
358 {
359     if (_fbBase == 0)
360     {
361 	THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the "
362 			    "pixel data source for image file "
363 			    "\"" << _outputFile.fileName() << "\".");
364     }
365 
366     if (_writeY && !_writeC)
367     {
368 	//
369 	// We are writing only luminance; filtering
370 	// and subsampling are not necessary.
371 	//
372 
373 	for (int i = 0; i < numScanLines; ++i)
374 	{
375 	    //
376 	    // Copy the next scan line from the caller's
377 	    // frame buffer into _tmpBuf.
378 	    //
379 
380 	    for (int j = 0; j < _width; ++j)
381 	    {
382 		_tmpBuf[j] = _fbBase[_fbYStride * _currentScanLine +
383 				     _fbXStride * (j + _xMin)];
384 	    }
385 
386 	    //
387 	    // Convert the scan line from RGB to luminance/chroma,
388 	    // and store the result in the output file.
389 	    //
390 
391 	    RGBAtoYCA (_yw, _width, _writeA, _tmpBuf, _tmpBuf);
392 	    _outputFile.writePixels (1);
393 
394 	    ++_linesConverted;
395 
396 	    if (_lineOrder == INCREASING_Y)
397 		++_currentScanLine;
398 	    else
399 		--_currentScanLine;
400 	}
401     }
402     else
403     {
404 	//
405 	// We are writing chroma; the pixels must be filtered and subsampled.
406 	//
407 
408 	for (int i = 0; i < numScanLines; ++i)
409 	{
410 	    //
411 	    // Copy the next scan line from the caller's
412 	    // frame buffer into _tmpBuf.
413 	    //
414 
415 	    for (int j = 0; j < _width; ++j)
416 	    {
417 		_tmpBuf[j + N2] = _fbBase[_fbYStride * _currentScanLine +
418 					  _fbXStride * (j + _xMin)];
419 	    }
420 
421 	    //
422 	    // Convert the scan line from RGB to luminance/chroma.
423 	    //
424 
425 	    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 	    padTmpBuf ();
433 
434 	    //
435 	    // Filter and subsample the scan line's chroma channels
436 	    // horizontally; store the result in _buf.
437 	    //
438 
439 	    rotateBuffers();
440 	    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 	    if (_linesConverted == 0)
448 	    {
449 		for (int j = 0; j < N2; ++j)
450 		    duplicateLastBuffer();
451 	    }
452 
453 	    ++_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 	    if (_linesConverted > N2)
463 		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 	    if (_linesConverted >= _height)
472 	    {
473 		for (int j = 0; j < N2 - _height; ++j)
474 		    duplicateLastBuffer();
475 
476 		duplicateSecondToLastBuffer();
477 		++_linesConverted;
478 		decimateChromaVertAndWriteScanLine();
479 
480 		for (int j = 1; j < min (_height, N2); ++j)
481 		{
482 		    duplicateLastBuffer();
483 		    ++_linesConverted;
484 		    decimateChromaVertAndWriteScanLine();
485 		}
486 	    }
487 
488 	    if (_lineOrder == INCREASING_Y)
489 		++_currentScanLine;
490 	    else
491 		--_currentScanLine;
492 	}
493     }
494 }
495 
496 
497 int
currentScanLine() const498 RgbaOutputFile::ToYca::currentScanLine () const
499 {
500     return _currentScanLine;
501 }
502 
503 
504 void
padTmpBuf()505 RgbaOutputFile::ToYca::padTmpBuf ()
506 {
507     for (int i = 0; i < N2; ++i)
508     {
509 	_tmpBuf[i] = _tmpBuf[N2];
510 	_tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
511     }
512 }
513 
514 
515 void
rotateBuffers()516 RgbaOutputFile::ToYca::rotateBuffers ()
517 {
518     Rgba *tmp = _buf[0];
519 
520     for (int i = 0; i < N - 1; ++i)
521 	_buf[i] = _buf[i + 1];
522 
523     _buf[N - 1] = tmp;
524 }
525 
526 
527 void
duplicateLastBuffer()528 RgbaOutputFile::ToYca::duplicateLastBuffer ()
529 {
530     rotateBuffers();
531     memcpy (_buf[N - 1], _buf[N - 2], _width * sizeof (Rgba));
532 }
533 
534 
535 void
duplicateSecondToLastBuffer()536 RgbaOutputFile::ToYca::duplicateSecondToLastBuffer ()
537 {
538     rotateBuffers();
539     memcpy (_buf[N - 1], _buf[N - 3], _width * sizeof (Rgba));
540 }
541 
542 
543 void
decimateChromaVertAndWriteScanLine()544 RgbaOutputFile::ToYca::decimateChromaVertAndWriteScanLine ()
545 {
546     if (_linesConverted & 1)
547 	memcpy (_tmpBuf, _buf[N2], _width * sizeof (Rgba));
548     else
549 	decimateChromaVert (_width, _buf, _tmpBuf);
550 
551     if (_writeY && _writeC)
552 	roundYCA (_width, _roundY, _roundC, _tmpBuf, _tmpBuf);
553 
554     _outputFile.writePixels (1);
555 }
556 
557 
RgbaOutputFile(const char name[],const Header & header,RgbaChannels rgbaChannels,int numThreads)558 RgbaOutputFile::RgbaOutputFile (const char name[],
559 				const Header &header,
560 				RgbaChannels rgbaChannels,
561                                 int numThreads):
562     _outputFile (0),
563     _toYca (0)
564 {
565     Header hd (header);
566     insertChannels (hd, rgbaChannels);
567     _outputFile = new OutputFile (name, hd, numThreads);
568 
569     if (rgbaChannels & (WRITE_Y | WRITE_C))
570 	_toYca = new ToYca (*_outputFile, rgbaChannels);
571 }
572 
573 
RgbaOutputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::OStream & os,const Header & header,RgbaChannels rgbaChannels,int numThreads)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 {
581     Header hd (header);
582     insertChannels (hd, rgbaChannels);
583     _outputFile = new OutputFile (os, hd, numThreads);
584 
585     if (rgbaChannels & (WRITE_Y | WRITE_C))
586 	_toYca = new ToYca (*_outputFile, rgbaChannels);
587 }
588 
589 
RgbaOutputFile(const char name[],const IMATH_NAMESPACE::Box2i & displayWindow,const IMATH_NAMESPACE::Box2i & dataWindow,RgbaChannels rgbaChannels,float pixelAspectRatio,const IMATH_NAMESPACE::V2f screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression,int numThreads)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 {
603     Header hd (displayWindow,
604 	       dataWindow.isEmpty()? displayWindow: dataWindow,
605 	       pixelAspectRatio,
606 	       screenWindowCenter,
607 	       screenWindowWidth,
608 	       lineOrder,
609 	       compression);
610 
611     insertChannels (hd, rgbaChannels);
612     _outputFile = new OutputFile (name, hd, numThreads);
613 
614     if (rgbaChannels & (WRITE_Y | WRITE_C))
615 	_toYca = new ToYca (*_outputFile, rgbaChannels);
616 }
617 
618 
RgbaOutputFile(const char name[],int width,int height,RgbaChannels rgbaChannels,float pixelAspectRatio,const IMATH_NAMESPACE::V2f screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression,int numThreads)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 {
632     Header hd (width,
633 	       height,
634 	       pixelAspectRatio,
635 	       screenWindowCenter,
636 	       screenWindowWidth,
637 	       lineOrder,
638 	       compression);
639 
640     insertChannels (hd, rgbaChannels);
641     _outputFile = new OutputFile (name, hd, numThreads);
642 
643     if (rgbaChannels & (WRITE_Y | WRITE_C))
644 	_toYca = new ToYca (*_outputFile, rgbaChannels);
645 }
646 
647 
~RgbaOutputFile()648 RgbaOutputFile::~RgbaOutputFile ()
649 {
650     delete _toYca;
651     delete _outputFile;
652 }
653 
654 
655 void
setFrameBuffer(const Rgba * base,size_t xStride,size_t yStride)656 RgbaOutputFile::setFrameBuffer (const Rgba *base,
657 				size_t xStride,
658 				size_t yStride)
659 {
660     if (_toYca)
661     {
662 	Lock lock (*_toYca);
663 	_toYca->setFrameBuffer (base, xStride, yStride);
664     }
665     else
666     {
667 	size_t xs = xStride * sizeof (Rgba);
668 	size_t ys = yStride * sizeof (Rgba);
669 
670 	FrameBuffer fb;
671 
672 	fb.insert ("R", Slice (HALF, (char *) &base[0].r, xs, ys));
673 	fb.insert ("G", Slice (HALF, (char *) &base[0].g, xs, ys));
674 	fb.insert ("B", Slice (HALF, (char *) &base[0].b, xs, ys));
675 	fb.insert ("A", Slice (HALF, (char *) &base[0].a, xs, ys));
676 
677 	_outputFile->setFrameBuffer (fb);
678     }
679 }
680 
681 
682 void
writePixels(int numScanLines)683 RgbaOutputFile::writePixels (int numScanLines)
684 {
685     if (_toYca)
686     {
687 	Lock lock (*_toYca);
688 	_toYca->writePixels (numScanLines);
689     }
690     else
691     {
692 	_outputFile->writePixels (numScanLines);
693     }
694 }
695 
696 
697 int
currentScanLine() const698 RgbaOutputFile::currentScanLine () const
699 {
700     if (_toYca)
701     {
702 	Lock lock (*_toYca);
703 	return _toYca->currentScanLine();
704     }
705     else
706     {
707 	return _outputFile->currentScanLine();
708     }
709 }
710 
711 
712 const Header &
header() const713 RgbaOutputFile::header () const
714 {
715     return _outputFile->header();
716 }
717 
718 
719 const FrameBuffer &
frameBuffer() const720 RgbaOutputFile::frameBuffer () const
721 {
722     return _outputFile->frameBuffer();
723 }
724 
725 
726 const IMATH_NAMESPACE::Box2i &
displayWindow() const727 RgbaOutputFile::displayWindow () const
728 {
729     return _outputFile->header().displayWindow();
730 }
731 
732 
733 const IMATH_NAMESPACE::Box2i &
dataWindow() const734 RgbaOutputFile::dataWindow () const
735 {
736     return _outputFile->header().dataWindow();
737 }
738 
739 
740 float
pixelAspectRatio() const741 RgbaOutputFile::pixelAspectRatio () const
742 {
743     return _outputFile->header().pixelAspectRatio();
744 }
745 
746 
747 const IMATH_NAMESPACE::V2f
screenWindowCenter() const748 RgbaOutputFile::screenWindowCenter () const
749 {
750     return _outputFile->header().screenWindowCenter();
751 }
752 
753 
754 float
screenWindowWidth() const755 RgbaOutputFile::screenWindowWidth () const
756 {
757     return _outputFile->header().screenWindowWidth();
758 }
759 
760 
761 LineOrder
lineOrder() const762 RgbaOutputFile::lineOrder () const
763 {
764     return _outputFile->header().lineOrder();
765 }
766 
767 
768 Compression
compression() const769 RgbaOutputFile::compression () const
770 {
771     return _outputFile->header().compression();
772 }
773 
774 
775 RgbaChannels
channels() const776 RgbaOutputFile::channels () const
777 {
778     return rgbaChannels (_outputFile->header().channels());
779 }
780 
781 
782 void
updatePreviewImage(const PreviewRgba newPixels[])783 RgbaOutputFile::updatePreviewImage (const PreviewRgba newPixels[])
784 {
785     _outputFile->updatePreviewImage (newPixels);
786 }
787 
788 
789 void
setYCRounding(unsigned int roundY,unsigned int roundC)790 RgbaOutputFile::setYCRounding (unsigned int roundY, unsigned int roundC)
791 {
792     if (_toYca)
793     {
794 	Lock lock (*_toYca);
795 	_toYca->setYCRounding (roundY, roundC);
796     }
797 }
798 
799 
800 void
breakScanLine(int y,int offset,int length,char c)801 RgbaOutputFile::breakScanLine  (int y, int offset, int length, char c)
802 {
803     _outputFile->breakScanLine (y, offset, length, c);
804 }
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 
FromYca(InputFile & inputFile,RgbaChannels rgbaChannels)849 RgbaInputFile::FromYca::FromYca (InputFile &inputFile,
850 				 RgbaChannels rgbaChannels)
851 :
852     _inputFile (inputFile)
853 {
854     _readC = (rgbaChannels & WRITE_C)? true: false;
855 
856     const Box2i dw = _inputFile.header().dataWindow();
857 
858     _xMin = dw.min.x;
859     _yMin = dw.min.y;
860     _yMax = dw.max.y;
861     _width  = dw.max.x - dw.min.x + 1;
862     _height = dw.max.y - dw.min.y + 1;
863     _currentScanLine = dw.min.y - N - 2;
864     _lineOrder = _inputFile.header().lineOrder();
865     _yw = ywFromHeader (_inputFile.header());
866 
867     ptrdiff_t pad = cachePadding (_width * sizeof (Rgba)) / sizeof (Rgba);
868 
869     _bufBase = new Rgba[(_width + pad) * (N + 2 + 3)];
870 
871     for (int i = 0; i < N + 2; ++i)
872 	_buf1[i] = _bufBase + (i * (_width + pad));
873 
874     for (int i = 0; i < 3; ++i)
875 	_buf2[i] = _bufBase + ((i + N + 2) * (_width + pad));
876 
877     _tmpBuf = new Rgba[_width + N - 1];
878 
879     _fbBase = 0;
880     _fbXStride = 0;
881     _fbYStride = 0;
882 }
883 
884 
~FromYca()885 RgbaInputFile::FromYca::~FromYca ()
886 {
887     delete [] _bufBase;
888     delete [] _tmpBuf;
889 }
890 
891 
892 void
setFrameBuffer(Rgba * base,size_t xStride,size_t yStride,const string & channelNamePrefix)893 RgbaInputFile::FromYca::setFrameBuffer (Rgba *base,
894 					size_t xStride,
895 					size_t yStride,
896 					const string &channelNamePrefix)
897 {
898     if (_fbBase == 0)
899     {
900 	FrameBuffer fb;
901 
902 	fb.insert (channelNamePrefix + "Y",
903 		   Slice (HALF,					// type
904 			  (char *) &_tmpBuf[N2 - _xMin].g,	// base
905 			  sizeof (Rgba),			// xStride
906 			  0,					// yStride
907 			  1,					// xSampling
908 			  1,					// ySampling
909 			  0.5));				// fillValue
910 
911 	if (_readC)
912 	{
913 	    fb.insert (channelNamePrefix + "RY",
914 		       Slice (HALF,				// type
915 			      (char *) &_tmpBuf[N2 - _xMin].r,	// base
916 			      sizeof (Rgba) * 2,		// xStride
917 			      0,				// yStride
918 			      2,				// xSampling
919 			      2,				// ySampling
920 			      0.0));				// fillValue
921 
922 	    fb.insert (channelNamePrefix + "BY",
923 		       Slice (HALF,				// type
924 			      (char *) &_tmpBuf[N2 - _xMin].b,	// base
925 			      sizeof (Rgba) * 2,		// xStride
926 			      0,				// yStride
927 			      2,				// xSampling
928 			      2,				// ySampling
929 			      0.0));				// fillValue
930 	}
931 
932 	fb.insert (channelNamePrefix + "A",
933 		   Slice (HALF,					// type
934 			  (char *) &_tmpBuf[N2 - _xMin].a,	// base
935 			  sizeof (Rgba),			// xStride
936 			  0,					// yStride
937 			  1,					// xSampling
938 			  1,					// ySampling
939 			  1.0));				// fillValue
940 
941 	_inputFile.setFrameBuffer (fb);
942     }
943 
944     _fbBase = base;
945     _fbXStride = xStride;
946     _fbYStride = yStride;
947 }
948 
949 
950 void
readPixels(int scanLine1,int scanLine2)951 RgbaInputFile::FromYca::readPixels (int scanLine1, int scanLine2)
952 {
953     int minY = min (scanLine1, scanLine2);
954     int maxY = max (scanLine1, scanLine2);
955 
956     if (_lineOrder == INCREASING_Y)
957     {
958 	for (int y = minY; y <= maxY; ++y)
959 	    readPixels (y);
960     }
961     else
962     {
963 	for (int y = maxY; y >= minY; --y)
964 	    readPixels (y);
965     }
966 }
967 
968 
969 void
readPixels(int scanLine)970 RgbaInputFile::FromYca::readPixels (int scanLine)
971 {
972     if (_fbBase == 0)
973     {
974 	THROW (IEX_NAMESPACE::ArgExc, "No frame buffer was specified as the "
975 			    "pixel data destination for image file "
976 			    "\"" << _inputFile.fileName() << "\".");
977     }
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     int dy = scanLine - _currentScanLine;
1009 
1010     if (abs (dy) < N + 2)
1011 	rotateBuf1 (dy);
1012 
1013     if (abs (dy) < 3)
1014 	rotateBuf2 (dy);
1015 
1016     if (dy < 0)
1017     {
1018 	{
1019 	    int n = min (-dy, N + 2);
1020 	    int yMin = scanLine - N2 - 1;
1021 
1022 	    for (int i = n - 1; i >= 0; --i)
1023 		readYCAScanLine (yMin + i, _buf1[i]);
1024 	}
1025 
1026 	{
1027 	    int n = min (-dy, 3);
1028 
1029 	    for (int i = 0; i < n; ++i)
1030 	    {
1031 		if ((scanLine + i) & 1)
1032 		{
1033 		    YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
1034 		}
1035 		else
1036 		{
1037 		    reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
1038 		    YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
1039 		}
1040 	    }
1041 	}
1042     }
1043     else
1044     {
1045 	{
1046 	    int n = min (dy, N + 2);
1047 	    int yMax = scanLine + N2 + 1;
1048 
1049 	    for (int i = n - 1; i >= 0; --i)
1050 		readYCAScanLine (yMax - i, _buf1[N + 1 - i]);
1051 	}
1052 
1053 	{
1054 	    int n = min (dy, 3);
1055 
1056 	    for (int i = 2; i > 2 - n; --i)
1057 	    {
1058 		if ((scanLine + i) & 1)
1059 		{
1060 		    YCAtoRGBA (_yw, _width, _buf1[N2 + i], _buf2[i]);
1061 		}
1062 		else
1063 		{
1064 		    reconstructChromaVert (_width, _buf1 + i, _buf2[i]);
1065 		    YCAtoRGBA (_yw, _width, _buf2[i], _buf2[i]);
1066 		}
1067 	    }
1068 	}
1069     }
1070 
1071     fixSaturation (_yw, _width, _buf2, _tmpBuf);
1072 
1073     for (int i = 0; i < _width; ++i)
1074 	_fbBase[_fbYStride * scanLine + _fbXStride * (i + _xMin)] = _tmpBuf[i];
1075 
1076     _currentScanLine = scanLine;
1077 }
1078 
1079 
1080 void
rotateBuf1(int d)1081 RgbaInputFile::FromYca::rotateBuf1 (int d)
1082 {
1083     d = modp (d, N + 2);
1084 
1085     Rgba *tmp[N + 2];
1086 
1087     for (int i = 0; i < N + 2; ++i)
1088 	tmp[i] = _buf1[i];
1089 
1090     for (int i = 0; i < N + 2; ++i)
1091 	_buf1[i] = tmp[(i + d) % (N + 2)];
1092 }
1093 
1094 
1095 void
rotateBuf2(int d)1096 RgbaInputFile::FromYca::rotateBuf2 (int d)
1097 {
1098     d = modp (d, 3);
1099 
1100     Rgba *tmp[3];
1101 
1102     for (int i = 0; i < 3; ++i)
1103 	tmp[i] = _buf2[i];
1104 
1105     for (int i = 0; i < 3; ++i)
1106 	_buf2[i] = tmp[(i + d) % 3];
1107 }
1108 
1109 
1110 void
readYCAScanLine(int y,Rgba * buf)1111 RgbaInputFile::FromYca::readYCAScanLine (int y, Rgba *buf)
1112 {
1113     //
1114     // Clamp y.
1115     //
1116 
1117     if (y < _yMin)
1118 	y = _yMin;
1119     else if (y > _yMax)
1120 	y = _yMax - 1;
1121 
1122     //
1123     // Read scan line y into _tmpBuf.
1124     //
1125 
1126     _inputFile.readPixels (y);
1127 
1128     //
1129     // Reconstruct missing chroma samples and copy
1130     // the scan line into buf.
1131     //
1132 
1133     if (!_readC)
1134     {
1135 	for (int i = 0; i < _width; ++i)
1136 	{
1137 	    _tmpBuf[i + N2].r = 0;
1138 	    _tmpBuf[i + N2].b = 0;
1139 	}
1140     }
1141 
1142     if (y & 1)
1143     {
1144 	memcpy (buf, _tmpBuf + N2, _width * sizeof (Rgba));
1145     }
1146     else
1147     {
1148 	padTmpBuf();
1149 	reconstructChromaHoriz (_width, _tmpBuf, buf);
1150     }
1151 }
1152 
1153 
1154 void
padTmpBuf()1155 RgbaInputFile::FromYca::padTmpBuf ()
1156 {
1157     for (int i = 0; i < N2; ++i)
1158     {
1159 	_tmpBuf[i] = _tmpBuf[N2];
1160 	_tmpBuf[_width + N2 + i] = _tmpBuf[_width + N2 - 2];
1161     }
1162 }
1163 
1164 
RgbaInputFile(const char name[],int numThreads)1165 RgbaInputFile::RgbaInputFile (const char name[], int numThreads):
1166     _inputFile (new InputFile (name, numThreads)),
1167     _fromYca (0),
1168     _channelNamePrefix ("")
1169 {
1170     RgbaChannels rgbaChannels = channels();
1171 
1172     if (rgbaChannels & (WRITE_Y | WRITE_C))
1173 	_fromYca = new FromYca (*_inputFile, rgbaChannels);
1174 }
1175 
1176 
RgbaInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)1177 RgbaInputFile::RgbaInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
1178     _inputFile (new InputFile (is, numThreads)),
1179     _fromYca (0),
1180     _channelNamePrefix ("")
1181 {
1182     RgbaChannels rgbaChannels = channels();
1183 
1184     if (rgbaChannels & (WRITE_Y | WRITE_C))
1185 	_fromYca = new FromYca (*_inputFile, rgbaChannels);
1186 }
1187 
1188 
RgbaInputFile(const char name[],const string & layerName,int numThreads)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 {
1197     RgbaChannels rgbaChannels = channels();
1198 
1199     if (rgbaChannels & (WRITE_Y | WRITE_C))
1200 	_fromYca = new FromYca (*_inputFile, rgbaChannels);
1201 }
1202 
1203 
RgbaInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,const string & layerName,int numThreads)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 {
1212     RgbaChannels rgbaChannels = channels();
1213 
1214     if (rgbaChannels & (WRITE_Y | WRITE_C))
1215 	_fromYca = new FromYca (*_inputFile, rgbaChannels);
1216 }
1217 
1218 
~RgbaInputFile()1219 RgbaInputFile::~RgbaInputFile ()
1220 {
1221     delete _inputFile;
1222     delete _fromYca;
1223 }
1224 
1225 
1226 void
setFrameBuffer(Rgba * base,size_t xStride,size_t yStride)1227 RgbaInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
1228 {
1229     if (_fromYca)
1230     {
1231 	Lock lock (*_fromYca);
1232 	_fromYca->setFrameBuffer (base, xStride, yStride, _channelNamePrefix);
1233     }
1234     else
1235     {
1236 	size_t xs = xStride * sizeof (Rgba);
1237 	size_t ys = yStride * sizeof (Rgba);
1238 
1239 	FrameBuffer fb;
1240 
1241 	fb.insert (_channelNamePrefix + "R",
1242 		   Slice (HALF,
1243 			  (char *) &base[0].r,
1244 			  xs, ys,
1245 			  1, 1,		// xSampling, ySampling
1246 			  0.0));	// fillValue
1247 
1248 	fb.insert (_channelNamePrefix + "G",
1249 		   Slice (HALF,
1250 			  (char *) &base[0].g,
1251 			  xs, ys,
1252 			  1, 1,		// xSampling, ySampling
1253 			  0.0));	// fillValue
1254 
1255 	fb.insert (_channelNamePrefix + "B",
1256 		   Slice (HALF,
1257 			  (char *) &base[0].b,
1258 			  xs, ys,
1259 			  1, 1,		// xSampling, ySampling
1260 			  0.0));	// fillValue
1261 
1262 	fb.insert (_channelNamePrefix + "A",
1263 		   Slice (HALF,
1264 			  (char *) &base[0].a,
1265 			  xs, ys,
1266 			  1, 1,		// xSampling, ySampling
1267 			  1.0));	// fillValue
1268 
1269 	_inputFile->setFrameBuffer (fb);
1270     }
1271 }
1272 
1273 
1274 void
setLayerName(const string & layerName)1275 RgbaInputFile::setLayerName (const string &layerName)
1276 {
1277     delete _fromYca;
1278     _fromYca = 0;
1279 
1280     _channelNamePrefix = prefixFromLayerName (layerName, _inputFile->header());
1281 
1282     RgbaChannels rgbaChannels = channels();
1283 
1284     if (rgbaChannels & (WRITE_Y | WRITE_C))
1285 	_fromYca = new FromYca (*_inputFile, rgbaChannels);
1286 
1287     FrameBuffer fb;
1288     _inputFile->setFrameBuffer (fb);
1289 }
1290 
1291 
1292 void
readPixels(int scanLine1,int scanLine2)1293 RgbaInputFile::readPixels (int scanLine1, int scanLine2)
1294 {
1295     if (_fromYca)
1296     {
1297 	Lock lock (*_fromYca);
1298 	_fromYca->readPixels (scanLine1, scanLine2);
1299     }
1300     else
1301     {
1302 	_inputFile->readPixels (scanLine1, scanLine2);
1303     }
1304 }
1305 
1306 
1307 void
readPixels(int scanLine)1308 RgbaInputFile::readPixels (int scanLine)
1309 {
1310     readPixels (scanLine, scanLine);
1311 }
1312 
1313 
1314 bool
isComplete() const1315 RgbaInputFile::isComplete () const
1316 {
1317     return _inputFile->isComplete();
1318 }
1319 
1320 
1321 const Header &
header() const1322 RgbaInputFile::header () const
1323 {
1324     return _inputFile->header();
1325 }
1326 
1327 
1328 const char *
fileName() const1329 RgbaInputFile::fileName () const
1330 {
1331     return _inputFile->fileName();
1332 }
1333 
1334 
1335 const FrameBuffer &
frameBuffer() const1336 RgbaInputFile::frameBuffer () const
1337 {
1338     return _inputFile->frameBuffer();
1339 }
1340 
1341 
1342 const IMATH_NAMESPACE::Box2i &
displayWindow() const1343 RgbaInputFile::displayWindow () const
1344 {
1345     return _inputFile->header().displayWindow();
1346 }
1347 
1348 
1349 const IMATH_NAMESPACE::Box2i &
dataWindow() const1350 RgbaInputFile::dataWindow () const
1351 {
1352     return _inputFile->header().dataWindow();
1353 }
1354 
1355 
1356 float
pixelAspectRatio() const1357 RgbaInputFile::pixelAspectRatio () const
1358 {
1359     return _inputFile->header().pixelAspectRatio();
1360 }
1361 
1362 
1363 const IMATH_NAMESPACE::V2f
screenWindowCenter() const1364 RgbaInputFile::screenWindowCenter () const
1365 {
1366     return _inputFile->header().screenWindowCenter();
1367 }
1368 
1369 
1370 float
screenWindowWidth() const1371 RgbaInputFile::screenWindowWidth () const
1372 {
1373     return _inputFile->header().screenWindowWidth();
1374 }
1375 
1376 
1377 LineOrder
lineOrder() const1378 RgbaInputFile::lineOrder () const
1379 {
1380     return _inputFile->header().lineOrder();
1381 }
1382 
1383 
1384 Compression
compression() const1385 RgbaInputFile::compression () const
1386 {
1387     return _inputFile->header().compression();
1388 }
1389 
1390 
1391 RgbaChannels
channels() const1392 RgbaInputFile::channels () const
1393 {
1394     return rgbaChannels (_inputFile->header().channels(), _channelNamePrefix);
1395 }
1396 
1397 
1398 int
version() const1399 RgbaInputFile::version () const
1400 {
1401     return _inputFile->version();
1402 }
1403 
1404 
1405 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1406