Home | History | Annotate | Download | only in IlmImf
      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 &
     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 
     78 AcesOutputFile::Data::Data ():
     79     rgbaFile (0)
     80 {
     81     // empty
     82 }
     83 
     84 
     85 AcesOutputFile::Data::~Data ()
     86 {
     87     delete rgbaFile;
     88 }
     89 
     90 
     91 namespace {
     92 
     93 void
     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 
    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 
    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 
    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 
    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 
    233 AcesOutputFile::~AcesOutputFile ()
    234 {
    235     delete _data;
    236 }
    237 
    238 
    239 void
    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
    250 AcesOutputFile::writePixels (int numScanLines)
    251 {
    252     _data->rgbaFile->writePixels (numScanLines);
    253 }
    254 
    255 
    256 int
    257 AcesOutputFile::currentScanLine () const
    258 {
    259     return _data->rgbaFile->currentScanLine();
    260 }
    261 
    262 
    263 const Header &
    264 AcesOutputFile::header () const
    265 {
    266     return _data->rgbaFile->header();
    267 }
    268 
    269 
    270 const Imath::Box2i &
    271 AcesOutputFile::displayWindow () const
    272 {
    273     return _data->rgbaFile->displayWindow();
    274 }
    275 
    276 
    277 const Imath::Box2i &
    278 AcesOutputFile::dataWindow () const
    279 {
    280     return _data->rgbaFile->dataWindow();
    281 }
    282 
    283 
    284 float
    285 AcesOutputFile::pixelAspectRatio () const
    286 {
    287     return _data->rgbaFile->pixelAspectRatio();
    288 }
    289 
    290 
    291 const Imath::V2f
    292 AcesOutputFile::screenWindowCenter () const
    293 {
    294     return _data->rgbaFile->screenWindowCenter();
    295 }
    296 
    297 
    298 float
    299 AcesOutputFile::screenWindowWidth () const
    300 {
    301     return _data->rgbaFile->screenWindowWidth();
    302 }
    303 
    304 
    305 LineOrder
    306 AcesOutputFile::lineOrder () const
    307 {
    308     return _data->rgbaFile->lineOrder();
    309 }
    310 
    311 
    312 Compression
    313 AcesOutputFile::compression () const
    314 {
    315     return _data->rgbaFile->compression();
    316 }
    317 
    318 
    319 RgbaChannels
    320 AcesOutputFile::channels () const
    321 {
    322     return _data->rgbaFile->channels();
    323 }
    324 
    325 
    326 void
    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 
    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 
    368 AcesInputFile::Data::~Data ()
    369 {
    370     delete rgbaFile;
    371 }
    372 
    373 
    374 void
    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 
    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 
    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 
    485 AcesInputFile::~AcesInputFile ()
    486 {
    487     delete _data;
    488 }
    489 
    490 
    491 void
    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
    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
    543 AcesInputFile::readPixels (int scanLine)
    544 {
    545     readPixels (scanLine, scanLine);
    546 }
    547 
    548 
    549 const Header &
    550 AcesInputFile::header () const
    551 {
    552     return _data->rgbaFile->header();
    553 }
    554 
    555 
    556 const Imath::Box2i &
    557 AcesInputFile::displayWindow () const
    558 {
    559     return _data->rgbaFile->displayWindow();
    560 }
    561 
    562 
    563 const Imath::Box2i &
    564 AcesInputFile::dataWindow () const
    565 {
    566     return _data->rgbaFile->dataWindow();
    567 }
    568 
    569 
    570 float
    571 AcesInputFile::pixelAspectRatio () const
    572 {
    573     return _data->rgbaFile->pixelAspectRatio();
    574 }
    575 
    576 
    577 const Imath::V2f
    578 AcesInputFile::screenWindowCenter () const
    579 {
    580     return _data->rgbaFile->screenWindowCenter();
    581 }
    582 
    583 
    584 float
    585 AcesInputFile::screenWindowWidth () const
    586 {
    587     return _data->rgbaFile->screenWindowWidth();
    588 }
    589 
    590 
    591 LineOrder
    592 AcesInputFile::lineOrder () const
    593 {
    594     return _data->rgbaFile->lineOrder();
    595 }
    596 
    597 
    598 Compression
    599 AcesInputFile::compression () const
    600 {
    601     return _data->rgbaFile->compression();
    602 }
    603 
    604 
    605 RgbaChannels
    606 AcesInputFile::channels () const
    607 {
    608     return _data->rgbaFile->channels();
    609 }
    610 
    611 
    612 const char *
    613 AcesInputFile::fileName () const
    614 {
    615     return _data->rgbaFile->fileName();
    616 }
    617 
    618 
    619 bool
    620 AcesInputFile::isComplete () const
    621 {
    622     return _data->rgbaFile->isComplete();
    623 }
    624 
    625 
    626 int
    627 AcesInputFile::version () const
    628 {
    629     return _data->rgbaFile->version();
    630 }
    631 
    632 } // namespace Imf
    633