root/3rdparty/openexr/IlmImf/ImfAcesFile.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. acesChromaticities
  2. checkCompression
  3. writePixels
  4. currentScanLine
  5. header
  6. displayWindow
  7. dataWindow
  8. pixelAspectRatio
  9. screenWindowCenter
  10. screenWindowWidth
  11. lineOrder
  12. compression
  13. channels
  14. updatePreviewImage
  15. mustConvertColor
  16. initColorConversion
  17. setFrameBuffer
  18. readPixels
  19. readPixels
  20. header
  21. displayWindow
  22. dataWindow
  23. pixelAspectRatio
  24. screenWindowCenter
  25. screenWindowWidth
  26. lineOrder
  27. compression
  28. channels
  29. fileName
  30. isComplete
  31. version

///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007, Industrial Light & Magic, a division of Lucas
// Digital Ltd. LLC
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
// *       Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// *       Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// *       Neither the name of Industrial Light & Magic nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//
//      ACES image file I/O.
//
//-----------------------------------------------------------------------------

#include <ImfAcesFile.h>
#include <ImfRgbaFile.h>
#include <ImfStandardAttributes.h>
#include <Iex.h>
#include <algorithm> // for std::max()

using namespace std;
using namespace Imath;
using namespace Iex;

namespace Imf {


const Chromaticities &
acesChromaticities ()
{
    static const Chromaticities acesChr
        (V2f (0.73470,  0.26530),       // red
         V2f (0.00000,  1.00000),       // green
         V2f (0.00010, -0.07700),       // blue
         V2f (0.32168,  0.33767));      // white

    return acesChr;
}


class AcesOutputFile::Data
{
  public:

     Data();
    ~Data();

    RgbaOutputFile *    rgbaFile;
};


AcesOutputFile::Data::Data ():
    rgbaFile (0)
{
    // empty
}


AcesOutputFile::Data::~Data ()
{
    delete rgbaFile;
}


namespace {

void
checkCompression (Compression compression)
{
    //
    // Not all compression methods are allowed in ACES files.
    //

    switch (compression)
    {
      case NO_COMPRESSION:
      case PIZ_COMPRESSION:
      case B44A_COMPRESSION:
    break;

      default:
    throw ArgExc ("Invalid compression type for ACES file.");
    }
}

} // namespace


AcesOutputFile::AcesOutputFile
    (const std::string &name,
     const Header &header,
     RgbaChannels rgbaChannels,
     int numThreads)
:
    _data (new Data)
{
    checkCompression (header.compression());

    Header newHeader = header;
    addChromaticities (newHeader, acesChromaticities());
    addAdoptedNeutral (newHeader, acesChromaticities().white);

    _data->rgbaFile = new RgbaOutputFile (name.c_str(),
                      newHeader,
                      rgbaChannels,
                      numThreads);

    _data->rgbaFile->setYCRounding (7, 6);
}


AcesOutputFile::AcesOutputFile
    (OStream &os,
     const Header &header,
     RgbaChannels rgbaChannels,
     int numThreads)
:
    _data (new Data)
{
    checkCompression (header.compression());

    Header newHeader = header;
    addChromaticities (newHeader, acesChromaticities());
    addAdoptedNeutral (newHeader, acesChromaticities().white);

    _data->rgbaFile = new RgbaOutputFile (os,
                      header,
                      rgbaChannels,
                      numThreads);

    _data->rgbaFile->setYCRounding (7, 6);
}


AcesOutputFile::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)
:
    _data (new Data)
{
    checkCompression (compression);

    Header newHeader (displayWindow,
              dataWindow.isEmpty()? displayWindow: dataWindow,
              pixelAspectRatio,
              screenWindowCenter,
              screenWindowWidth,
              lineOrder,
              compression);

    addChromaticities (newHeader, acesChromaticities());
    addAdoptedNeutral (newHeader, acesChromaticities().white);

    _data->rgbaFile = new RgbaOutputFile (name.c_str(),
                      newHeader,
                      rgbaChannels,
                      numThreads);

    _data->rgbaFile->setYCRounding (7, 6);
}


AcesOutputFile::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)
:
    _data (new Data)
{
    checkCompression (compression);

    Header newHeader (width,
              height,
              pixelAspectRatio,
              screenWindowCenter,
              screenWindowWidth,
              lineOrder,
              compression);

    addChromaticities (newHeader, acesChromaticities());
    addAdoptedNeutral (newHeader, acesChromaticities().white);

    _data->rgbaFile = new RgbaOutputFile (name.c_str(),
                      newHeader,
                      rgbaChannels,
                      numThreads);

    _data->rgbaFile->setYCRounding (7, 6);
}


AcesOutputFile::~AcesOutputFile ()
{
    delete _data;
}


void
AcesOutputFile::setFrameBuffer
    (const Rgba *base,
     size_t xStride,
     size_t yStride)
{
    _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
}


void
AcesOutputFile::writePixels (int numScanLines)
{
    _data->rgbaFile->writePixels (numScanLines);
}


int
AcesOutputFile::currentScanLine () const
{
    return _data->rgbaFile->currentScanLine();
}


const Header &
AcesOutputFile::header () const
{
    return _data->rgbaFile->header();
}


const Imath::Box2i &
AcesOutputFile::displayWindow () const
{
    return _data->rgbaFile->displayWindow();
}


const Imath::Box2i &
AcesOutputFile::dataWindow () const
{
    return _data->rgbaFile->dataWindow();
}


float
AcesOutputFile::pixelAspectRatio () const
{
    return _data->rgbaFile->pixelAspectRatio();
}


const Imath::V2f
AcesOutputFile::screenWindowCenter () const
{
    return _data->rgbaFile->screenWindowCenter();
}


float
AcesOutputFile::screenWindowWidth () const
{
    return _data->rgbaFile->screenWindowWidth();
}


LineOrder
AcesOutputFile::lineOrder () const
{
    return _data->rgbaFile->lineOrder();
}


Compression
AcesOutputFile::compression () const
{
    return _data->rgbaFile->compression();
}


RgbaChannels
AcesOutputFile::channels () const
{
    return _data->rgbaFile->channels();
}


void
AcesOutputFile::updatePreviewImage (const PreviewRgba pixels[])
{
    _data->rgbaFile->updatePreviewImage (pixels);
}


class AcesInputFile::Data
{
  public:

     Data();
    ~Data();

    void                initColorConversion ();

    RgbaInputFile *     rgbaFile;

    Rgba *              fbBase;
    size_t              fbXStride;
    size_t              fbYStride;
    int                 minX;
    int                 maxX;

    bool                mustConvertColor;
    M44f                fileToAces;
};


AcesInputFile::Data::Data ():
    rgbaFile (0),
    fbBase (0),
    fbXStride (0),
    fbYStride (0),
    minX (0),
    maxX (0),
    mustConvertColor (false)
{
    // empty
}


AcesInputFile::Data::~Data ()
{
    delete rgbaFile;
}


void
AcesInputFile::Data::initColorConversion ()
{
    const Header &header = rgbaFile->header();

    Chromaticities fileChr;

    if (hasChromaticities (header))
    fileChr = chromaticities (header);

    V2f fileNeutral = fileChr.white;

    if (hasAdoptedNeutral (header))
    fileNeutral = adoptedNeutral (header);

    const Chromaticities acesChr = acesChromaticities();

    V2f acesNeutral = acesChr.white;

    if (fileChr.red == acesChr.red &&
    fileChr.green == acesChr.green &&
    fileChr.blue == acesChr.blue &&
    fileChr.white == acesChr.white &&
    fileNeutral == acesNeutral)
    {
    //
    // The file already contains ACES data,
    // color conversion is not necessary.

    return;
    }

    mustConvertColor = true;
    minX = header.dataWindow().min.x;
    maxX = header.dataWindow().max.x;

    //
    // Create a matrix that transforms colors from the
    // RGB space of the input file into the ACES space
    // using a color adaptation transform to move the
    // white point.
    //

    //
    // We'll need the Bradford cone primary matrix and its inverse
    //

    static const M44f bradfordCPM
        (0.895100, -0.750200,  0.038900,  0.000000,
         0.266400,  1.713500, -0.068500,  0.000000,
        -0.161400,  0.036700,  1.029600,  0.000000,
         0.000000,  0.000000,  0.000000,  1.000000);

    const static M44f inverseBradfordCPM
        (0.986993,  0.432305, -0.008529,  0.000000,
        -0.147054,  0.518360,  0.040043,  0.000000,
         0.159963,  0.049291,  0.968487,  0.000000,
         0.000000,  0.000000,  0.000000,  1.000000);

    //
    // Convert the white points of the two RGB spaces to XYZ
    //

    float fx = fileNeutral.x;
    float fy = fileNeutral.y;
    V3f fileNeutralXYZ (fx / fy, 1, (1 - fx - fy) / fy);

    float ax = acesNeutral.x;
    float ay = acesNeutral.y;
    V3f acesNeutralXYZ (ax / ay, 1, (1 - ax - ay) / ay);

    //
    // Compute the Bradford transformation matrix
    //

    V3f ratio ((acesNeutralXYZ * bradfordCPM) /
           (fileNeutralXYZ * bradfordCPM));

    M44f ratioMat (ratio[0], 0,        0,        0,
           0,        ratio[1], 0,        0,
           0,        0,        ratio[2], 0,
           0,        0,        0,        1);

    M44f bradfordTrans = bradfordCPM *
                         ratioMat *
             inverseBradfordCPM;

    //
    // Build a combined file-RGB-to-ACES-RGB conversion matrix
    //

    fileToAces = RGBtoXYZ (fileChr, 1) * bradfordTrans * XYZtoRGB (acesChr, 1);
}


AcesInputFile::AcesInputFile (const std::string &name, int numThreads):
    _data (new Data)
{
    _data->rgbaFile = new RgbaInputFile (name.c_str(), numThreads);
    _data->initColorConversion();
}


AcesInputFile::AcesInputFile (IStream &is, int numThreads):
    _data (new Data)
{
    _data->rgbaFile = new RgbaInputFile (is, numThreads);
    _data->initColorConversion();
}


AcesInputFile::~AcesInputFile ()
{
    delete _data;
}


void
AcesInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
{
    _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
    _data->fbBase = base;
    _data->fbXStride = xStride;
    _data->fbYStride = yStride;
}


void
AcesInputFile::readPixels (int scanLine1, int scanLine2)
{
    //
    // Copy the pixels from the RgbaInputFile into the frame buffer.
    //

    _data->rgbaFile->readPixels (scanLine1, scanLine2);

    //
    // If the RGB space of the input file is not the same as the ACES
    // RGB space, then the pixels in the frame buffer must be transformed
    // into the ACES RGB space.
    //

    if (!_data->mustConvertColor)
    return;

    int minY = min (scanLine1, scanLine2);
    int maxY = max (scanLine1, scanLine2);

    for (int y = minY; y <= maxY; ++y)
    {
    Rgba *base = _data->fbBase +
             _data->fbXStride * _data->minX +
             _data->fbYStride * y;

    for (int x = _data->minX; x <= _data->maxX; ++x)
    {
        V3f aces = V3f (base->r, base->g, base->b) * _data->fileToAces;

        base->r = aces[0];
        base->g = aces[1];
        base->b = aces[2];

        base += _data->fbXStride;
    }
    }
}


void
AcesInputFile::readPixels (int scanLine)
{
    readPixels (scanLine, scanLine);
}


const Header &
AcesInputFile::header () const
{
    return _data->rgbaFile->header();
}


const Imath::Box2i &
AcesInputFile::displayWindow () const
{
    return _data->rgbaFile->displayWindow();
}


const Imath::Box2i &
AcesInputFile::dataWindow () const
{
    return _data->rgbaFile->dataWindow();
}


float
AcesInputFile::pixelAspectRatio () const
{
    return _data->rgbaFile->pixelAspectRatio();
}


const Imath::V2f
AcesInputFile::screenWindowCenter () const
{
    return _data->rgbaFile->screenWindowCenter();
}


float
AcesInputFile::screenWindowWidth () const
{
    return _data->rgbaFile->screenWindowWidth();
}


LineOrder
AcesInputFile::lineOrder () const
{
    return _data->rgbaFile->lineOrder();
}


Compression
AcesInputFile::compression () const
{
    return _data->rgbaFile->compression();
}


RgbaChannels
AcesInputFile::channels () const
{
    return _data->rgbaFile->channels();
}


const char *
AcesInputFile::fileName () const
{
    return _data->rgbaFile->fileName();
}


bool
AcesInputFile::isComplete () const
{
    return _data->rgbaFile->isComplete();
}


int
AcesInputFile::version () const
{
    return _data->rgbaFile->version();
}

} // namespace Imf

/* [<][>][^][v][top][bottom][index][help] */