Path: blob/master/3rdparty/openexr/IlmImf/ImfAcesFile.cpp
16337 views
///////////////////////////////////////////////////////////////////////////1//2// Copyright (c) 2007, Industrial Light & Magic, a division of Lucas3// Digital Ltd. LLC4//5// All rights reserved.6//7// Redistribution and use in source and binary forms, with or without8// modification, are permitted provided that the following conditions are9// met:10// * Redistributions of source code must retain the above copyright11// notice, this list of conditions and the following disclaimer.12// * Redistributions in binary form must reproduce the above13// copyright notice, this list of conditions and the following disclaimer14// in the documentation and/or other materials provided with the15// distribution.16// * Neither the name of Industrial Light & Magic nor the names of17// its contributors may be used to endorse or promote products derived18// from this software without specific prior written permission.19//20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.31//32///////////////////////////////////////////////////////////////////////////3334//-----------------------------------------------------------------------------35//36// ACES image file I/O.37//38//-----------------------------------------------------------------------------3940#include <ImfAcesFile.h>41#include <ImfRgbaFile.h>42#include <ImfStandardAttributes.h>43#include <Iex.h>44#include <algorithm> // for std::max()4546using namespace std;47using namespace Imath;48using namespace Iex;4950namespace Imf {515253const Chromaticities &54acesChromaticities ()55{56static const Chromaticities acesChr57(V2f (0.73470, 0.26530), // red58V2f (0.00000, 1.00000), // green59V2f (0.00010, -0.07700), // blue60V2f (0.32168, 0.33767)); // white6162return acesChr;63}646566class AcesOutputFile::Data67{68public:6970Data();71~Data();7273RgbaOutputFile * rgbaFile;74};757677AcesOutputFile::Data::Data ():78rgbaFile (0)79{80// empty81}828384AcesOutputFile::Data::~Data ()85{86delete rgbaFile;87}888990namespace {9192void93checkCompression (Compression compression)94{95//96// Not all compression methods are allowed in ACES files.97//9899switch (compression)100{101case NO_COMPRESSION:102case PIZ_COMPRESSION:103case B44A_COMPRESSION:104break;105106default:107throw ArgExc ("Invalid compression type for ACES file.");108}109}110111} // namespace112113114AcesOutputFile::AcesOutputFile115(const std::string &name,116const Header &header,117RgbaChannels rgbaChannels,118int numThreads)119:120_data (new Data)121{122checkCompression (header.compression());123124Header newHeader = header;125addChromaticities (newHeader, acesChromaticities());126addAdoptedNeutral (newHeader, acesChromaticities().white);127128_data->rgbaFile = new RgbaOutputFile (name.c_str(),129newHeader,130rgbaChannels,131numThreads);132133_data->rgbaFile->setYCRounding (7, 6);134}135136137AcesOutputFile::AcesOutputFile138(OStream &os,139const Header &header,140RgbaChannels rgbaChannels,141int numThreads)142:143_data (new Data)144{145checkCompression (header.compression());146147Header newHeader = header;148addChromaticities (newHeader, acesChromaticities());149addAdoptedNeutral (newHeader, acesChromaticities().white);150151_data->rgbaFile = new RgbaOutputFile (os,152header,153rgbaChannels,154numThreads);155156_data->rgbaFile->setYCRounding (7, 6);157}158159160AcesOutputFile::AcesOutputFile161(const std::string &name,162const Imath::Box2i &displayWindow,163const Imath::Box2i &dataWindow,164RgbaChannels rgbaChannels,165float pixelAspectRatio,166const Imath::V2f screenWindowCenter,167float screenWindowWidth,168LineOrder lineOrder,169Compression compression,170int numThreads)171:172_data (new Data)173{174checkCompression (compression);175176Header newHeader (displayWindow,177dataWindow.isEmpty()? displayWindow: dataWindow,178pixelAspectRatio,179screenWindowCenter,180screenWindowWidth,181lineOrder,182compression);183184addChromaticities (newHeader, acesChromaticities());185addAdoptedNeutral (newHeader, acesChromaticities().white);186187_data->rgbaFile = new RgbaOutputFile (name.c_str(),188newHeader,189rgbaChannels,190numThreads);191192_data->rgbaFile->setYCRounding (7, 6);193}194195196AcesOutputFile::AcesOutputFile197(const std::string &name,198int width,199int height,200RgbaChannels rgbaChannels,201float pixelAspectRatio,202const Imath::V2f screenWindowCenter,203float screenWindowWidth,204LineOrder lineOrder,205Compression compression,206int numThreads)207:208_data (new Data)209{210checkCompression (compression);211212Header newHeader (width,213height,214pixelAspectRatio,215screenWindowCenter,216screenWindowWidth,217lineOrder,218compression);219220addChromaticities (newHeader, acesChromaticities());221addAdoptedNeutral (newHeader, acesChromaticities().white);222223_data->rgbaFile = new RgbaOutputFile (name.c_str(),224newHeader,225rgbaChannels,226numThreads);227228_data->rgbaFile->setYCRounding (7, 6);229}230231232AcesOutputFile::~AcesOutputFile ()233{234delete _data;235}236237238void239AcesOutputFile::setFrameBuffer240(const Rgba *base,241size_t xStride,242size_t yStride)243{244_data->rgbaFile->setFrameBuffer (base, xStride, yStride);245}246247248void249AcesOutputFile::writePixels (int numScanLines)250{251_data->rgbaFile->writePixels (numScanLines);252}253254255int256AcesOutputFile::currentScanLine () const257{258return _data->rgbaFile->currentScanLine();259}260261262const Header &263AcesOutputFile::header () const264{265return _data->rgbaFile->header();266}267268269const Imath::Box2i &270AcesOutputFile::displayWindow () const271{272return _data->rgbaFile->displayWindow();273}274275276const Imath::Box2i &277AcesOutputFile::dataWindow () const278{279return _data->rgbaFile->dataWindow();280}281282283float284AcesOutputFile::pixelAspectRatio () const285{286return _data->rgbaFile->pixelAspectRatio();287}288289290const Imath::V2f291AcesOutputFile::screenWindowCenter () const292{293return _data->rgbaFile->screenWindowCenter();294}295296297float298AcesOutputFile::screenWindowWidth () const299{300return _data->rgbaFile->screenWindowWidth();301}302303304LineOrder305AcesOutputFile::lineOrder () const306{307return _data->rgbaFile->lineOrder();308}309310311Compression312AcesOutputFile::compression () const313{314return _data->rgbaFile->compression();315}316317318RgbaChannels319AcesOutputFile::channels () const320{321return _data->rgbaFile->channels();322}323324325void326AcesOutputFile::updatePreviewImage (const PreviewRgba pixels[])327{328_data->rgbaFile->updatePreviewImage (pixels);329}330331332class AcesInputFile::Data333{334public:335336Data();337~Data();338339void initColorConversion ();340341RgbaInputFile * rgbaFile;342343Rgba * fbBase;344size_t fbXStride;345size_t fbYStride;346int minX;347int maxX;348349bool mustConvertColor;350M44f fileToAces;351};352353354AcesInputFile::Data::Data ():355rgbaFile (0),356fbBase (0),357fbXStride (0),358fbYStride (0),359minX (0),360maxX (0),361mustConvertColor (false)362{363// empty364}365366367AcesInputFile::Data::~Data ()368{369delete rgbaFile;370}371372373void374AcesInputFile::Data::initColorConversion ()375{376const Header &header = rgbaFile->header();377378Chromaticities fileChr;379380if (hasChromaticities (header))381fileChr = chromaticities (header);382383V2f fileNeutral = fileChr.white;384385if (hasAdoptedNeutral (header))386fileNeutral = adoptedNeutral (header);387388const Chromaticities acesChr = acesChromaticities();389390V2f acesNeutral = acesChr.white;391392if (fileChr.red == acesChr.red &&393fileChr.green == acesChr.green &&394fileChr.blue == acesChr.blue &&395fileChr.white == acesChr.white &&396fileNeutral == acesNeutral)397{398//399// The file already contains ACES data,400// color conversion is not necessary.401402return;403}404405mustConvertColor = true;406minX = header.dataWindow().min.x;407maxX = header.dataWindow().max.x;408409//410// Create a matrix that transforms colors from the411// RGB space of the input file into the ACES space412// using a color adaptation transform to move the413// white point.414//415416//417// We'll need the Bradford cone primary matrix and its inverse418//419420static const M44f bradfordCPM421(0.895100, -0.750200, 0.038900, 0.000000,4220.266400, 1.713500, -0.068500, 0.000000,423-0.161400, 0.036700, 1.029600, 0.000000,4240.000000, 0.000000, 0.000000, 1.000000);425426const static M44f inverseBradfordCPM427(0.986993, 0.432305, -0.008529, 0.000000,428-0.147054, 0.518360, 0.040043, 0.000000,4290.159963, 0.049291, 0.968487, 0.000000,4300.000000, 0.000000, 0.000000, 1.000000);431432//433// Convert the white points of the two RGB spaces to XYZ434//435436float fx = fileNeutral.x;437float fy = fileNeutral.y;438V3f fileNeutralXYZ (fx / fy, 1, (1 - fx - fy) / fy);439440float ax = acesNeutral.x;441float ay = acesNeutral.y;442V3f acesNeutralXYZ (ax / ay, 1, (1 - ax - ay) / ay);443444//445// Compute the Bradford transformation matrix446//447448V3f ratio ((acesNeutralXYZ * bradfordCPM) /449(fileNeutralXYZ * bradfordCPM));450451M44f ratioMat (ratio[0], 0, 0, 0,4520, ratio[1], 0, 0,4530, 0, ratio[2], 0,4540, 0, 0, 1);455456M44f bradfordTrans = bradfordCPM *457ratioMat *458inverseBradfordCPM;459460//461// Build a combined file-RGB-to-ACES-RGB conversion matrix462//463464fileToAces = RGBtoXYZ (fileChr, 1) * bradfordTrans * XYZtoRGB (acesChr, 1);465}466467468AcesInputFile::AcesInputFile (const std::string &name, int numThreads):469_data (new Data)470{471_data->rgbaFile = new RgbaInputFile (name.c_str(), numThreads);472_data->initColorConversion();473}474475476AcesInputFile::AcesInputFile (IStream &is, int numThreads):477_data (new Data)478{479_data->rgbaFile = new RgbaInputFile (is, numThreads);480_data->initColorConversion();481}482483484AcesInputFile::~AcesInputFile ()485{486delete _data;487}488489490void491AcesInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)492{493_data->rgbaFile->setFrameBuffer (base, xStride, yStride);494_data->fbBase = base;495_data->fbXStride = xStride;496_data->fbYStride = yStride;497}498499500void501AcesInputFile::readPixels (int scanLine1, int scanLine2)502{503//504// Copy the pixels from the RgbaInputFile into the frame buffer.505//506507_data->rgbaFile->readPixels (scanLine1, scanLine2);508509//510// If the RGB space of the input file is not the same as the ACES511// RGB space, then the pixels in the frame buffer must be transformed512// into the ACES RGB space.513//514515if (!_data->mustConvertColor)516return;517518int minY = min (scanLine1, scanLine2);519int maxY = max (scanLine1, scanLine2);520521for (int y = minY; y <= maxY; ++y)522{523Rgba *base = _data->fbBase +524_data->fbXStride * _data->minX +525_data->fbYStride * y;526527for (int x = _data->minX; x <= _data->maxX; ++x)528{529V3f aces = V3f (base->r, base->g, base->b) * _data->fileToAces;530531base->r = aces[0];532base->g = aces[1];533base->b = aces[2];534535base += _data->fbXStride;536}537}538}539540541void542AcesInputFile::readPixels (int scanLine)543{544readPixels (scanLine, scanLine);545}546547548const Header &549AcesInputFile::header () const550{551return _data->rgbaFile->header();552}553554555const Imath::Box2i &556AcesInputFile::displayWindow () const557{558return _data->rgbaFile->displayWindow();559}560561562const Imath::Box2i &563AcesInputFile::dataWindow () const564{565return _data->rgbaFile->dataWindow();566}567568569float570AcesInputFile::pixelAspectRatio () const571{572return _data->rgbaFile->pixelAspectRatio();573}574575576const Imath::V2f577AcesInputFile::screenWindowCenter () const578{579return _data->rgbaFile->screenWindowCenter();580}581582583float584AcesInputFile::screenWindowWidth () const585{586return _data->rgbaFile->screenWindowWidth();587}588589590LineOrder591AcesInputFile::lineOrder () const592{593return _data->rgbaFile->lineOrder();594}595596597Compression598AcesInputFile::compression () const599{600return _data->rgbaFile->compression();601}602603604RgbaChannels605AcesInputFile::channels () const606{607return _data->rgbaFile->channels();608}609610611const char *612AcesInputFile::fileName () const613{614return _data->rgbaFile->fileName();615}616617618bool619AcesInputFile::isComplete () const620{621return _data->rgbaFile->isComplete();622}623624625int626AcesInputFile::version () const627{628return _data->rgbaFile->version();629}630631} // namespace Imf632633634