ImfAcesFile.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  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. // ACES image file I/O.
  37. //
  38. //-----------------------------------------------------------------------------
  39. #include <ImfAcesFile.h>
  40. #include <ImfRgbaFile.h>
  41. #include <ImfStandardAttributes.h>
  42. #include <Iex.h>
  43. #include <algorithm>
  44. using namespace std;
  45. using namespace IMATH_NAMESPACE;
  46. using namespace IEX_NAMESPACE;
  47. #include "ImfNamespace.h"
  48. OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
  49. const Chromaticities &
  50. acesChromaticities ()
  51. {
  52. static const Chromaticities acesChr
  53. (V2f (0.73470, 0.26530), // red
  54. V2f (0.00000, 1.00000), // green
  55. V2f (0.00010, -0.07700), // blue
  56. V2f (0.32168, 0.33767)); // white
  57. return acesChr;
  58. }
  59. class AcesOutputFile::Data
  60. {
  61. public:
  62. Data();
  63. ~Data();
  64. RgbaOutputFile * rgbaFile;
  65. };
  66. AcesOutputFile::Data::Data ():
  67. rgbaFile (0)
  68. {
  69. // empty
  70. }
  71. AcesOutputFile::Data::~Data ()
  72. {
  73. delete rgbaFile;
  74. }
  75. namespace {
  76. void
  77. checkCompression (Compression compression)
  78. {
  79. //
  80. // Not all compression methods are allowed in ACES files.
  81. //
  82. switch (compression)
  83. {
  84. case NO_COMPRESSION:
  85. case PIZ_COMPRESSION:
  86. case B44A_COMPRESSION:
  87. break;
  88. default:
  89. throw ArgExc ("Invalid compression type for ACES file.");
  90. }
  91. }
  92. } // namespace
  93. AcesOutputFile::AcesOutputFile
  94. (const std::string &name,
  95. const Header &header,
  96. RgbaChannels rgbaChannels,
  97. int numThreads)
  98. :
  99. _data (new Data)
  100. {
  101. checkCompression (header.compression());
  102. Header newHeader = header;
  103. addChromaticities (newHeader, acesChromaticities());
  104. addAdoptedNeutral (newHeader, acesChromaticities().white);
  105. _data->rgbaFile = new RgbaOutputFile (name.c_str(),
  106. newHeader,
  107. rgbaChannels,
  108. numThreads);
  109. _data->rgbaFile->setYCRounding (7, 6);
  110. }
  111. AcesOutputFile::AcesOutputFile
  112. (OPENEXR_IMF_INTERNAL_NAMESPACE::OStream &os,
  113. const Header &header,
  114. RgbaChannels rgbaChannels,
  115. int numThreads)
  116. :
  117. _data (new Data)
  118. {
  119. checkCompression (header.compression());
  120. Header newHeader = header;
  121. addChromaticities (newHeader, acesChromaticities());
  122. addAdoptedNeutral (newHeader, acesChromaticities().white);
  123. _data->rgbaFile = new RgbaOutputFile (os,
  124. header,
  125. rgbaChannels,
  126. numThreads);
  127. _data->rgbaFile->setYCRounding (7, 6);
  128. }
  129. AcesOutputFile::AcesOutputFile
  130. (const std::string &name,
  131. const IMATH_NAMESPACE::Box2i &displayWindow,
  132. const IMATH_NAMESPACE::Box2i &dataWindow,
  133. RgbaChannels rgbaChannels,
  134. float pixelAspectRatio,
  135. const IMATH_NAMESPACE::V2f screenWindowCenter,
  136. float screenWindowWidth,
  137. LineOrder lineOrder,
  138. Compression compression,
  139. int numThreads)
  140. :
  141. _data (new Data)
  142. {
  143. checkCompression (compression);
  144. Header newHeader (displayWindow,
  145. dataWindow.isEmpty()? displayWindow: dataWindow,
  146. pixelAspectRatio,
  147. screenWindowCenter,
  148. screenWindowWidth,
  149. lineOrder,
  150. compression);
  151. addChromaticities (newHeader, acesChromaticities());
  152. addAdoptedNeutral (newHeader, acesChromaticities().white);
  153. _data->rgbaFile = new RgbaOutputFile (name.c_str(),
  154. newHeader,
  155. rgbaChannels,
  156. numThreads);
  157. _data->rgbaFile->setYCRounding (7, 6);
  158. }
  159. AcesOutputFile::AcesOutputFile
  160. (const std::string &name,
  161. int width,
  162. int height,
  163. RgbaChannels rgbaChannels,
  164. float pixelAspectRatio,
  165. const IMATH_NAMESPACE::V2f screenWindowCenter,
  166. float screenWindowWidth,
  167. LineOrder lineOrder,
  168. Compression compression,
  169. int numThreads)
  170. :
  171. _data (new Data)
  172. {
  173. checkCompression (compression);
  174. Header newHeader (width,
  175. height,
  176. pixelAspectRatio,
  177. screenWindowCenter,
  178. screenWindowWidth,
  179. lineOrder,
  180. compression);
  181. addChromaticities (newHeader, acesChromaticities());
  182. addAdoptedNeutral (newHeader, acesChromaticities().white);
  183. _data->rgbaFile = new RgbaOutputFile (name.c_str(),
  184. newHeader,
  185. rgbaChannels,
  186. numThreads);
  187. _data->rgbaFile->setYCRounding (7, 6);
  188. }
  189. AcesOutputFile::~AcesOutputFile ()
  190. {
  191. delete _data;
  192. }
  193. void
  194. AcesOutputFile::setFrameBuffer
  195. (const Rgba *base,
  196. size_t xStride,
  197. size_t yStride)
  198. {
  199. _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
  200. }
  201. void
  202. AcesOutputFile::writePixels (int numScanLines)
  203. {
  204. _data->rgbaFile->writePixels (numScanLines);
  205. }
  206. int
  207. AcesOutputFile::currentScanLine () const
  208. {
  209. return _data->rgbaFile->currentScanLine();
  210. }
  211. const Header &
  212. AcesOutputFile::header () const
  213. {
  214. return _data->rgbaFile->header();
  215. }
  216. const IMATH_NAMESPACE::Box2i &
  217. AcesOutputFile::displayWindow () const
  218. {
  219. return _data->rgbaFile->displayWindow();
  220. }
  221. const IMATH_NAMESPACE::Box2i &
  222. AcesOutputFile::dataWindow () const
  223. {
  224. return _data->rgbaFile->dataWindow();
  225. }
  226. float
  227. AcesOutputFile::pixelAspectRatio () const
  228. {
  229. return _data->rgbaFile->pixelAspectRatio();
  230. }
  231. const IMATH_NAMESPACE::V2f
  232. AcesOutputFile::screenWindowCenter () const
  233. {
  234. return _data->rgbaFile->screenWindowCenter();
  235. }
  236. float
  237. AcesOutputFile::screenWindowWidth () const
  238. {
  239. return _data->rgbaFile->screenWindowWidth();
  240. }
  241. LineOrder
  242. AcesOutputFile::lineOrder () const
  243. {
  244. return _data->rgbaFile->lineOrder();
  245. }
  246. Compression
  247. AcesOutputFile::compression () const
  248. {
  249. return _data->rgbaFile->compression();
  250. }
  251. RgbaChannels
  252. AcesOutputFile::channels () const
  253. {
  254. return _data->rgbaFile->channels();
  255. }
  256. void
  257. AcesOutputFile::updatePreviewImage (const PreviewRgba pixels[])
  258. {
  259. _data->rgbaFile->updatePreviewImage (pixels);
  260. }
  261. class AcesInputFile::Data
  262. {
  263. public:
  264. Data();
  265. ~Data();
  266. void initColorConversion ();
  267. RgbaInputFile * rgbaFile;
  268. Rgba * fbBase;
  269. size_t fbXStride;
  270. size_t fbYStride;
  271. int minX;
  272. int maxX;
  273. bool mustConvertColor;
  274. M44f fileToAces;
  275. };
  276. AcesInputFile::Data::Data ():
  277. rgbaFile (0),
  278. fbBase (0),
  279. fbXStride (0),
  280. fbYStride (0),
  281. minX (0),
  282. maxX (0),
  283. mustConvertColor (false)
  284. {
  285. // empty
  286. }
  287. AcesInputFile::Data::~Data ()
  288. {
  289. delete rgbaFile;
  290. }
  291. void
  292. AcesInputFile::Data::initColorConversion ()
  293. {
  294. const Header &header = rgbaFile->header();
  295. Chromaticities fileChr;
  296. if (hasChromaticities (header))
  297. fileChr = chromaticities (header);
  298. V2f fileNeutral = fileChr.white;
  299. if (hasAdoptedNeutral (header))
  300. fileNeutral = adoptedNeutral (header);
  301. const Chromaticities acesChr = acesChromaticities();
  302. V2f acesNeutral = acesChr.white;
  303. if (fileChr.red == acesChr.red &&
  304. fileChr.green == acesChr.green &&
  305. fileChr.blue == acesChr.blue &&
  306. fileChr.white == acesChr.white &&
  307. fileNeutral == acesNeutral)
  308. {
  309. //
  310. // The file already contains ACES data,
  311. // color conversion is not necessary.
  312. return;
  313. }
  314. mustConvertColor = true;
  315. minX = header.dataWindow().min.x;
  316. maxX = header.dataWindow().max.x;
  317. //
  318. // Create a matrix that transforms colors from the
  319. // RGB space of the input file into the ACES space
  320. // using a color adaptation transform to move the
  321. // white point.
  322. //
  323. //
  324. // We'll need the Bradford cone primary matrix and its inverse
  325. //
  326. static const M44f bradfordCPM
  327. (0.895100, -0.750200, 0.038900, 0.000000,
  328. 0.266400, 1.713500, -0.068500, 0.000000,
  329. -0.161400, 0.036700, 1.029600, 0.000000,
  330. 0.000000, 0.000000, 0.000000, 1.000000);
  331. const static M44f inverseBradfordCPM
  332. (0.986993, 0.432305, -0.008529, 0.000000,
  333. -0.147054, 0.518360, 0.040043, 0.000000,
  334. 0.159963, 0.049291, 0.968487, 0.000000,
  335. 0.000000, 0.000000, 0.000000, 1.000000);
  336. //
  337. // Convert the white points of the two RGB spaces to XYZ
  338. //
  339. float fx = fileNeutral.x;
  340. float fy = fileNeutral.y;
  341. V3f fileNeutralXYZ (fx / fy, 1, (1 - fx - fy) / fy);
  342. float ax = acesNeutral.x;
  343. float ay = acesNeutral.y;
  344. V3f acesNeutralXYZ (ax / ay, 1, (1 - ax - ay) / ay);
  345. //
  346. // Compute the Bradford transformation matrix
  347. //
  348. V3f ratio ((acesNeutralXYZ * bradfordCPM) /
  349. (fileNeutralXYZ * bradfordCPM));
  350. M44f ratioMat (ratio[0], 0, 0, 0,
  351. 0, ratio[1], 0, 0,
  352. 0, 0, ratio[2], 0,
  353. 0, 0, 0, 1);
  354. M44f bradfordTrans = bradfordCPM *
  355. ratioMat *
  356. inverseBradfordCPM;
  357. //
  358. // Build a combined file-RGB-to-ACES-RGB conversion matrix
  359. //
  360. fileToAces = RGBtoXYZ (fileChr, 1) * bradfordTrans * XYZtoRGB (acesChr, 1);
  361. }
  362. AcesInputFile::AcesInputFile (const std::string &name, int numThreads):
  363. _data (new Data)
  364. {
  365. _data->rgbaFile = new RgbaInputFile (name.c_str(), numThreads);
  366. _data->initColorConversion();
  367. }
  368. AcesInputFile::AcesInputFile (IStream &is, int numThreads):
  369. _data (new Data)
  370. {
  371. _data->rgbaFile = new RgbaInputFile (is, numThreads);
  372. _data->initColorConversion();
  373. }
  374. AcesInputFile::~AcesInputFile ()
  375. {
  376. delete _data;
  377. }
  378. void
  379. AcesInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
  380. {
  381. _data->rgbaFile->setFrameBuffer (base, xStride, yStride);
  382. _data->fbBase = base;
  383. _data->fbXStride = xStride;
  384. _data->fbYStride = yStride;
  385. }
  386. void
  387. AcesInputFile::readPixels (int scanLine1, int scanLine2)
  388. {
  389. //
  390. // Copy the pixels from the RgbaInputFile into the frame buffer.
  391. //
  392. _data->rgbaFile->readPixels (scanLine1, scanLine2);
  393. //
  394. // If the RGB space of the input file is not the same as the ACES
  395. // RGB space, then the pixels in the frame buffer must be transformed
  396. // into the ACES RGB space.
  397. //
  398. if (!_data->mustConvertColor)
  399. return;
  400. int minY = min (scanLine1, scanLine2);
  401. int maxY = max (scanLine1, scanLine2);
  402. for (int y = minY; y <= maxY; ++y)
  403. {
  404. Rgba *base = _data->fbBase +
  405. _data->fbXStride * _data->minX +
  406. _data->fbYStride * y;
  407. for (int x = _data->minX; x <= _data->maxX; ++x)
  408. {
  409. V3f aces = V3f (base->r, base->g, base->b) * _data->fileToAces;
  410. base->r = aces[0];
  411. base->g = aces[1];
  412. base->b = aces[2];
  413. base += _data->fbXStride;
  414. }
  415. }
  416. }
  417. void
  418. AcesInputFile::readPixels (int scanLine)
  419. {
  420. readPixels (scanLine, scanLine);
  421. }
  422. const Header &
  423. AcesInputFile::header () const
  424. {
  425. return _data->rgbaFile->header();
  426. }
  427. const IMATH_NAMESPACE::Box2i &
  428. AcesInputFile::displayWindow () const
  429. {
  430. return _data->rgbaFile->displayWindow();
  431. }
  432. const IMATH_NAMESPACE::Box2i &
  433. AcesInputFile::dataWindow () const
  434. {
  435. return _data->rgbaFile->dataWindow();
  436. }
  437. float
  438. AcesInputFile::pixelAspectRatio () const
  439. {
  440. return _data->rgbaFile->pixelAspectRatio();
  441. }
  442. const IMATH_NAMESPACE::V2f
  443. AcesInputFile::screenWindowCenter () const
  444. {
  445. return _data->rgbaFile->screenWindowCenter();
  446. }
  447. float
  448. AcesInputFile::screenWindowWidth () const
  449. {
  450. return _data->rgbaFile->screenWindowWidth();
  451. }
  452. LineOrder
  453. AcesInputFile::lineOrder () const
  454. {
  455. return _data->rgbaFile->lineOrder();
  456. }
  457. Compression
  458. AcesInputFile::compression () const
  459. {
  460. return _data->rgbaFile->compression();
  461. }
  462. RgbaChannels
  463. AcesInputFile::channels () const
  464. {
  465. return _data->rgbaFile->channels();
  466. }
  467. const char *
  468. AcesInputFile::fileName () const
  469. {
  470. return _data->rgbaFile->fileName();
  471. }
  472. bool
  473. AcesInputFile::isComplete () const
  474. {
  475. return _data->rgbaFile->isComplete();
  476. }
  477. int
  478. AcesInputFile::version () const
  479. {
  480. return _data->rgbaFile->version();
  481. }
  482. OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT