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