123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- ///////////////////////////////////////////////////////////////////////////
- //
- // 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>
- using namespace std;
- using namespace IMATH_NAMESPACE;
- using namespace IEX_NAMESPACE;
- #include "ImfNamespace.h"
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
- 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
- (OPENEXR_IMF_INTERNAL_NAMESPACE::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_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)
- :
- _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_NAMESPACE::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_NAMESPACE::Box2i &
- AcesOutputFile::displayWindow () const
- {
- return _data->rgbaFile->displayWindow();
- }
- const IMATH_NAMESPACE::Box2i &
- AcesOutputFile::dataWindow () const
- {
- return _data->rgbaFile->dataWindow();
- }
- float
- AcesOutputFile::pixelAspectRatio () const
- {
- return _data->rgbaFile->pixelAspectRatio();
- }
- const IMATH_NAMESPACE::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_NAMESPACE::Box2i &
- AcesInputFile::displayWindow () const
- {
- return _data->rgbaFile->displayWindow();
- }
- const IMATH_NAMESPACE::Box2i &
- AcesInputFile::dataWindow () const
- {
- return _data->rgbaFile->dataWindow();
- }
- float
- AcesInputFile::pixelAspectRatio () const
- {
- return _data->rgbaFile->pixelAspectRatio();
- }
- const IMATH_NAMESPACE::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();
- }
- OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
|