• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2007, 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 //	ACES image file I/O.
38 //
39 //-----------------------------------------------------------------------------
40 
41 #include <ImfAcesFile.h>
42 #include <ImfRgbaFile.h>
43 #include <ImfStandardAttributes.h>
44 #include <Iex.h>
45 #include <algorithm> // for std::max()
46 
47 using namespace std;
48 using namespace Imath;
49 using namespace Iex;
50 
51 namespace Imf {
52 
53 
54 const Chromaticities &
acesChromaticities()55 acesChromaticities ()
56 {
57     static const Chromaticities acesChr
58         (V2f (0.73470,  0.26530),	// red
59          V2f (0.00000,  1.00000),	// green
60          V2f (0.00010, -0.07700),	// blue
61          V2f (0.32168,  0.33767));	// white
62 
63     return acesChr;
64 }
65 
66 
67 class AcesOutputFile::Data
68 {
69   public:
70 
71      Data();
72     ~Data();
73 
74     RgbaOutputFile *	rgbaFile;
75 };
76 
77 
Data()78 AcesOutputFile::Data::Data ():
79     rgbaFile (0)
80 {
81     // empty
82 }
83 
84 
~Data()85 AcesOutputFile::Data::~Data ()
86 {
87     delete rgbaFile;
88 }
89 
90 
91 namespace {
92 
93 void
checkCompression(Compression compression)94 checkCompression (Compression compression)
95 {
96     //
97     // Not all compression methods are allowed in ACES files.
98     //
99 
100     switch (compression)
101     {
102       case NO_COMPRESSION:
103       case PIZ_COMPRESSION:
104       case B44A_COMPRESSION:
105     break;
106 
107       default:
108     throw ArgExc ("Invalid compression type for ACES file.");
109     }
110 }
111 
112 } // namespace
113 
114 
AcesOutputFile(const std::string & name,const Header & header,RgbaChannels rgbaChannels,int numThreads)115 AcesOutputFile::AcesOutputFile
116     (const std::string &name,
117      const Header &header,
118      RgbaChannels rgbaChannels,
119      int numThreads)
120 :
121     _data (new Data)
122 {
123     checkCompression (header.compression());
124 
125     Header newHeader = header;
126     addChromaticities (newHeader, acesChromaticities());
127     addAdoptedNeutral (newHeader, acesChromaticities().white);
128 
129     _data->rgbaFile = new RgbaOutputFile (name.c_str(),
130                       newHeader,
131                       rgbaChannels,
132                       numThreads);
133 
134     _data->rgbaFile->setYCRounding (7, 6);
135 }
136 
137 
AcesOutputFile(OStream & os,const Header & header,RgbaChannels rgbaChannels,int numThreads)138 AcesOutputFile::AcesOutputFile
139     (OStream &os,
140      const Header &header,
141      RgbaChannels rgbaChannels,
142      int numThreads)
143 :
144     _data (new Data)
145 {
146     checkCompression (header.compression());
147 
148     Header newHeader = header;
149     addChromaticities (newHeader, acesChromaticities());
150     addAdoptedNeutral (newHeader, acesChromaticities().white);
151 
152     _data->rgbaFile = new RgbaOutputFile (os,
153                       header,
154                       rgbaChannels,
155                       numThreads);
156 
157     _data->rgbaFile->setYCRounding (7, 6);
158 }
159 
160 
AcesOutputFile(const std::string & name,const Imath::Box2i & displayWindow,const Imath::Box2i & dataWindow,RgbaChannels rgbaChannels,float pixelAspectRatio,const Imath::V2f screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression,int numThreads)161 AcesOutputFile::AcesOutputFile
162     (const std::string &name,
163      const Imath::Box2i &displayWindow,
164      const Imath::Box2i &dataWindow,
165      RgbaChannels rgbaChannels,
166      float pixelAspectRatio,
167      const Imath::V2f screenWindowCenter,
168      float screenWindowWidth,
169      LineOrder lineOrder,
170      Compression compression,
171      int numThreads)
172 :
173     _data (new Data)
174 {
175     checkCompression (compression);
176 
177     Header newHeader (displayWindow,
178               dataWindow.isEmpty()? displayWindow: dataWindow,
179               pixelAspectRatio,
180               screenWindowCenter,
181               screenWindowWidth,
182               lineOrder,
183               compression);
184 
185     addChromaticities (newHeader, acesChromaticities());
186     addAdoptedNeutral (newHeader, acesChromaticities().white);
187 
188     _data->rgbaFile = new RgbaOutputFile (name.c_str(),
189                       newHeader,
190                       rgbaChannels,
191                       numThreads);
192 
193     _data->rgbaFile->setYCRounding (7, 6);
194 }
195 
196 
AcesOutputFile(const std::string & name,int width,int height,RgbaChannels rgbaChannels,float pixelAspectRatio,const Imath::V2f screenWindowCenter,float screenWindowWidth,LineOrder lineOrder,Compression compression,int numThreads)197 AcesOutputFile::AcesOutputFile
198     (const std::string &name,
199      int width,
200      int height,
201      RgbaChannels rgbaChannels,
202      float pixelAspectRatio,
203      const Imath::V2f screenWindowCenter,
204      float screenWindowWidth,
205      LineOrder lineOrder,
206      Compression compression,
207      int numThreads)
208 :
209     _data (new Data)
210 {
211     checkCompression (compression);
212 
213     Header newHeader (width,
214               height,
215               pixelAspectRatio,
216               screenWindowCenter,
217               screenWindowWidth,
218               lineOrder,
219               compression);
220 
221     addChromaticities (newHeader, acesChromaticities());
222     addAdoptedNeutral (newHeader, acesChromaticities().white);
223 
224     _data->rgbaFile = new RgbaOutputFile (name.c_str(),
225                       newHeader,
226                       rgbaChannels,
227                       numThreads);
228 
229     _data->rgbaFile->setYCRounding (7, 6);
230 }
231 
232 
~AcesOutputFile()233 AcesOutputFile::~AcesOutputFile ()
234 {
235     delete _data;
236 }
237 
238 
239 void
setFrameBuffer(const Rgba * base,size_t xStride,size_t yStride)240 AcesOutputFile::setFrameBuffer
241     (const Rgba *base,
242      size_t xStride,
243      size_t yStride)
244 {
245     _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
246 }
247 
248 
249 void
writePixels(int numScanLines)250 AcesOutputFile::writePixels (int numScanLines)
251 {
252     _data->rgbaFile->writePixels (numScanLines);
253 }
254 
255 
256 int
currentScanLine() const257 AcesOutputFile::currentScanLine () const
258 {
259     return _data->rgbaFile->currentScanLine();
260 }
261 
262 
263 const Header &
header() const264 AcesOutputFile::header () const
265 {
266     return _data->rgbaFile->header();
267 }
268 
269 
270 const Imath::Box2i &
displayWindow() const271 AcesOutputFile::displayWindow () const
272 {
273     return _data->rgbaFile->displayWindow();
274 }
275 
276 
277 const Imath::Box2i &
dataWindow() const278 AcesOutputFile::dataWindow () const
279 {
280     return _data->rgbaFile->dataWindow();
281 }
282 
283 
284 float
pixelAspectRatio() const285 AcesOutputFile::pixelAspectRatio () const
286 {
287     return _data->rgbaFile->pixelAspectRatio();
288 }
289 
290 
291 const Imath::V2f
screenWindowCenter() const292 AcesOutputFile::screenWindowCenter () const
293 {
294     return _data->rgbaFile->screenWindowCenter();
295 }
296 
297 
298 float
screenWindowWidth() const299 AcesOutputFile::screenWindowWidth () const
300 {
301     return _data->rgbaFile->screenWindowWidth();
302 }
303 
304 
305 LineOrder
lineOrder() const306 AcesOutputFile::lineOrder () const
307 {
308     return _data->rgbaFile->lineOrder();
309 }
310 
311 
312 Compression
compression() const313 AcesOutputFile::compression () const
314 {
315     return _data->rgbaFile->compression();
316 }
317 
318 
319 RgbaChannels
channels() const320 AcesOutputFile::channels () const
321 {
322     return _data->rgbaFile->channels();
323 }
324 
325 
326 void
updatePreviewImage(const PreviewRgba pixels[])327 AcesOutputFile::updatePreviewImage (const PreviewRgba pixels[])
328 {
329     _data->rgbaFile->updatePreviewImage (pixels);
330 }
331 
332 
333 class AcesInputFile::Data
334 {
335   public:
336 
337      Data();
338     ~Data();
339 
340     void		initColorConversion ();
341 
342     RgbaInputFile *	rgbaFile;
343 
344     Rgba *		fbBase;
345     size_t		fbXStride;
346     size_t		fbYStride;
347     int			minX;
348     int			maxX;
349 
350     bool		mustConvertColor;
351     M44f		fileToAces;
352 };
353 
354 
Data()355 AcesInputFile::Data::Data ():
356     rgbaFile (0),
357     fbBase (0),
358     fbXStride (0),
359     fbYStride (0),
360     minX (0),
361     maxX (0),
362     mustConvertColor (false)
363 {
364     // empty
365 }
366 
367 
~Data()368 AcesInputFile::Data::~Data ()
369 {
370     delete rgbaFile;
371 }
372 
373 
374 void
initColorConversion()375 AcesInputFile::Data::initColorConversion ()
376 {
377     const Header &header = rgbaFile->header();
378 
379     Chromaticities fileChr;
380 
381     if (hasChromaticities (header))
382     fileChr = chromaticities (header);
383 
384     V2f fileNeutral = fileChr.white;
385 
386     if (hasAdoptedNeutral (header))
387     fileNeutral = adoptedNeutral (header);
388 
389     const Chromaticities acesChr = acesChromaticities();
390 
391     V2f acesNeutral = acesChr.white;
392 
393     if (fileChr.red == acesChr.red &&
394     fileChr.green == acesChr.green &&
395     fileChr.blue == acesChr.blue &&
396     fileChr.white == acesChr.white &&
397     fileNeutral == acesNeutral)
398     {
399     //
400     // The file already contains ACES data,
401     // color conversion is not necessary.
402 
403     return;
404     }
405 
406     mustConvertColor = true;
407     minX = header.dataWindow().min.x;
408     maxX = header.dataWindow().max.x;
409 
410     //
411     // Create a matrix that transforms colors from the
412     // RGB space of the input file into the ACES space
413     // using a color adaptation transform to move the
414     // white point.
415     //
416 
417     //
418     // We'll need the Bradford cone primary matrix and its inverse
419     //
420 
421     static const M44f bradfordCPM
422         (0.895100, -0.750200,  0.038900,  0.000000,
423          0.266400,  1.713500, -0.068500,  0.000000,
424         -0.161400,  0.036700,  1.029600,  0.000000,
425          0.000000,  0.000000,  0.000000,  1.000000);
426 
427     const static M44f inverseBradfordCPM
428         (0.986993,  0.432305, -0.008529,  0.000000,
429         -0.147054,  0.518360,  0.040043,  0.000000,
430          0.159963,  0.049291,  0.968487,  0.000000,
431          0.000000,  0.000000,  0.000000,  1.000000);
432 
433     //
434     // Convert the white points of the two RGB spaces to XYZ
435     //
436 
437     float fx = fileNeutral.x;
438     float fy = fileNeutral.y;
439     V3f fileNeutralXYZ (fx / fy, 1, (1 - fx - fy) / fy);
440 
441     float ax = acesNeutral.x;
442     float ay = acesNeutral.y;
443     V3f acesNeutralXYZ (ax / ay, 1, (1 - ax - ay) / ay);
444 
445     //
446     // Compute the Bradford transformation matrix
447     //
448 
449     V3f ratio ((acesNeutralXYZ * bradfordCPM) /
450            (fileNeutralXYZ * bradfordCPM));
451 
452     M44f ratioMat (ratio[0], 0,        0,        0,
453            0,        ratio[1], 0,        0,
454            0,        0,        ratio[2], 0,
455            0,        0,        0,        1);
456 
457     M44f bradfordTrans = bradfordCPM *
458                          ratioMat *
459              inverseBradfordCPM;
460 
461     //
462     // Build a combined file-RGB-to-ACES-RGB conversion matrix
463     //
464 
465     fileToAces = RGBtoXYZ (fileChr, 1) * bradfordTrans * XYZtoRGB (acesChr, 1);
466 }
467 
468 
AcesInputFile(const std::string & name,int numThreads)469 AcesInputFile::AcesInputFile (const std::string &name, int numThreads):
470     _data (new Data)
471 {
472     _data->rgbaFile = new RgbaInputFile (name.c_str(), numThreads);
473     _data->initColorConversion();
474 }
475 
476 
AcesInputFile(IStream & is,int numThreads)477 AcesInputFile::AcesInputFile (IStream &is, int numThreads):
478     _data (new Data)
479 {
480     _data->rgbaFile = new RgbaInputFile (is, numThreads);
481     _data->initColorConversion();
482 }
483 
484 
~AcesInputFile()485 AcesInputFile::~AcesInputFile ()
486 {
487     delete _data;
488 }
489 
490 
491 void
setFrameBuffer(Rgba * base,size_t xStride,size_t yStride)492 AcesInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
493 {
494     _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
495     _data->fbBase = base;
496     _data->fbXStride = xStride;
497     _data->fbYStride = yStride;
498 }
499 
500 
501 void
readPixels(int scanLine1,int scanLine2)502 AcesInputFile::readPixels (int scanLine1, int scanLine2)
503 {
504     //
505     // Copy the pixels from the RgbaInputFile into the frame buffer.
506     //
507 
508     _data->rgbaFile->readPixels (scanLine1, scanLine2);
509 
510     //
511     // If the RGB space of the input file is not the same as the ACES
512     // RGB space, then the pixels in the frame buffer must be transformed
513     // into the ACES RGB space.
514     //
515 
516     if (!_data->mustConvertColor)
517     return;
518 
519     int minY = min (scanLine1, scanLine2);
520     int maxY = max (scanLine1, scanLine2);
521 
522     for (int y = minY; y <= maxY; ++y)
523     {
524     Rgba *base = _data->fbBase +
525              _data->fbXStride * _data->minX +
526              _data->fbYStride * y;
527 
528     for (int x = _data->minX; x <= _data->maxX; ++x)
529     {
530         V3f aces = V3f (base->r, base->g, base->b) * _data->fileToAces;
531 
532         base->r = aces[0];
533         base->g = aces[1];
534         base->b = aces[2];
535 
536         base += _data->fbXStride;
537     }
538     }
539 }
540 
541 
542 void
readPixels(int scanLine)543 AcesInputFile::readPixels (int scanLine)
544 {
545     readPixels (scanLine, scanLine);
546 }
547 
548 
549 const Header &
header() const550 AcesInputFile::header () const
551 {
552     return _data->rgbaFile->header();
553 }
554 
555 
556 const Imath::Box2i &
displayWindow() const557 AcesInputFile::displayWindow () const
558 {
559     return _data->rgbaFile->displayWindow();
560 }
561 
562 
563 const Imath::Box2i &
dataWindow() const564 AcesInputFile::dataWindow () const
565 {
566     return _data->rgbaFile->dataWindow();
567 }
568 
569 
570 float
pixelAspectRatio() const571 AcesInputFile::pixelAspectRatio () const
572 {
573     return _data->rgbaFile->pixelAspectRatio();
574 }
575 
576 
577 const Imath::V2f
screenWindowCenter() const578 AcesInputFile::screenWindowCenter () const
579 {
580     return _data->rgbaFile->screenWindowCenter();
581 }
582 
583 
584 float
screenWindowWidth() const585 AcesInputFile::screenWindowWidth () const
586 {
587     return _data->rgbaFile->screenWindowWidth();
588 }
589 
590 
591 LineOrder
lineOrder() const592 AcesInputFile::lineOrder () const
593 {
594     return _data->rgbaFile->lineOrder();
595 }
596 
597 
598 Compression
compression() const599 AcesInputFile::compression () const
600 {
601     return _data->rgbaFile->compression();
602 }
603 
604 
605 RgbaChannels
channels() const606 AcesInputFile::channels () const
607 {
608     return _data->rgbaFile->channels();
609 }
610 
611 
612 const char *
fileName() const613 AcesInputFile::fileName () const
614 {
615     return _data->rgbaFile->fileName();
616 }
617 
618 
619 bool
isComplete() const620 AcesInputFile::isComplete () const
621 {
622     return _data->rgbaFile->isComplete();
623 }
624 
625 
626 int
version() const627 AcesInputFile::version () const
628 {
629     return _data->rgbaFile->version();
630 }
631 
632 } // namespace Imf
633