/src/core/FileFormat3DL.cpp

http://github.com/imageworks/OpenColorIO · C++ · 638 lines · 391 code · 101 blank · 146 comment · 57 complexity · fe67625ce00da6a4f9170952147d97d3 MD5 · raw file

  1. /*
  2. Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
  3. All Rights Reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are
  6. met:
  7. * Redistributions of source code must retain the above copyright
  8. notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright
  10. notice, this list of conditions and the following disclaimer in the
  11. documentation and/or other materials provided with the distribution.
  12. * Neither the name of Sony Pictures Imageworks nor the names of its
  13. contributors may be used to endorse or promote products derived from
  14. this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <OpenColorIO/OpenColorIO.h>
  28. #include "FileTransform.h"
  29. #include "Lut1DOp.h"
  30. #include "Lut3DOp.h"
  31. #include "MathUtils.h"
  32. #include "ParseUtils.h"
  33. #include "pystring/pystring.h"
  34. #include <algorithm>
  35. #include <cmath>
  36. #include <cstdio>
  37. #include <sstream>
  38. /*
  39. // Discreet's Flame Lut Format
  40. // Use a loose interpretation of the format to allow other 3d luts that look
  41. // similar, but dont strictly adhere to the real definition.
  42. // If line starts with text or # skip it
  43. // If line is a bunch of ints (more than 3) , it's the 1d shaper lut
  44. // All remaining lines of size 3 int are data
  45. // cube size is determined from num entries
  46. // The bit depth of the shaper lut and the 3d lut need not be the same.
  47. Example 1, FLAME
  48. # Comment here
  49. 0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1023
  50. 0 0 0
  51. 0 0 100
  52. 0 0 200
  53. Example 2, LUSTRE
  54. #Tokens required by applications - do not edit
  55. 3DMESH
  56. Mesh 4 12
  57. 0 64 128 192 256 320 384 448 512 576 640 704 768 832 896 960 1023
  58. 0 17 17
  59. 0 0 88
  60. 0 0 157
  61. 9 101 197
  62. 0 118 308
  63. ...
  64. 4092 4094 4094
  65. #Tokens required by applications - do not edit
  66. LUT8
  67. gamma 1.0
  68. In this example, the 3D LUT has an input bit depth of 4 bits and an output
  69. bit depth of 12 bits. You use the input value to calculate the RGB triplet
  70. to be 17*17*17 (where 17=(2 to the power of 4)+1, and 4 is the input bit
  71. depth). The first triplet is the output value at (0,0,0);(0,0,1);...;
  72. (0,0,16) r,g,b coordinates; the second triplet is the output value at
  73. (0,1,0);(0,1,1);...;(0,1,16) r,g,b coordinates; and so on. You use the output
  74. bit depth to set the output bit depth range (12 bits or 0-4095).
  75. NoteLustre supports an input and output depth of 16 bits for 3D LUTs; however,
  76. in the processing pipeline, the BLACK_LEVEL to WHITE_LEVEL range is only 14
  77. bits. This means that even if the 3D LUT is 16 bits, it is normalized to
  78. fit the BLACK_LEVEL to WHITE_LEVEL range of Lustre.
  79. In Lustre, 3D LUT files can contain grids of 17 cubed, 33 cubed, and 65 cubed;
  80. however, Lustre converts 17 cubed and 65 cubed grids to 33 cubed for internal
  81. processing on the output (for rendering and calibration), but not on the input
  82. 3D LUT.
  83. */
  84. OCIO_NAMESPACE_ENTER
  85. {
  86. ////////////////////////////////////////////////////////////////
  87. namespace
  88. {
  89. class LocalCachedFile : public CachedFile
  90. {
  91. public:
  92. LocalCachedFile () :
  93. has1D(false),
  94. has3D(false)
  95. {
  96. lut1D = Lut1D::Create();
  97. lut3D = Lut3D::Create();
  98. };
  99. ~LocalCachedFile() {};
  100. bool has1D;
  101. bool has3D;
  102. Lut1DRcPtr lut1D;
  103. Lut3DRcPtr lut3D;
  104. };
  105. typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr;
  106. class LocalFileFormat : public FileFormat
  107. {
  108. public:
  109. ~LocalFileFormat() {};
  110. virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
  111. virtual CachedFileRcPtr Read(std::istream & istream) const;
  112. virtual void Write(const Baker & baker,
  113. const std::string & formatName,
  114. std::ostream & ostream) const;
  115. virtual void BuildFileOps(OpRcPtrVec & ops,
  116. const Config& config,
  117. const ConstContextRcPtr & context,
  118. CachedFileRcPtr untypedCachedFile,
  119. const FileTransform& fileTransform,
  120. TransformDirection dir) const;
  121. };
  122. // We use the maximum value found in the lut to infer
  123. // the bit depth. While this is fugly. We dont believe
  124. // there is a better way, looking at the file, to
  125. // determine this.
  126. //
  127. // Note: We allow for 2x overshoot in the luts.
  128. // As we dont allow for odd bit depths, this isnt a big deal.
  129. // So sizes from 1/2 max - 2x max are valid
  130. //
  131. // FILE EXPECTED MAX CORRECTLY DECODED IF MAX IN THIS RANGE
  132. // 8-bit 255 [0, 511]
  133. // 10-bit 1023 [512, 2047]
  134. // 12-bit 4095 [2048, 8191]
  135. // 14-bit 16383 [8192, 32767]
  136. // 16-bit 65535 [32768, 131071+]
  137. int GetLikelyLutBitDepth(int testval)
  138. {
  139. const int MIN_BIT_DEPTH = 8;
  140. const int MAX_BIT_DEPTH = 16;
  141. if(testval < 0) return -1;
  142. // Only test even bit depths
  143. for(int bitDepth = MIN_BIT_DEPTH;
  144. bitDepth <= MAX_BIT_DEPTH; bitDepth+=2)
  145. {
  146. int maxcode = static_cast<int>(pow(2.0,bitDepth));
  147. int adjustedMax = maxcode * 2 - 1;
  148. if(testval<=adjustedMax) return bitDepth;
  149. }
  150. return MAX_BIT_DEPTH;
  151. }
  152. int GetMaxValueFromIntegerBitDepth(int bitDepth)
  153. {
  154. return static_cast<int>( pow(2.0, bitDepth) ) - 1;
  155. }
  156. int GetClampedIntFromNormFloat(float val, float scale)
  157. {
  158. val = std::min(std::max(0.0f, val), 1.0f) * scale;
  159. return static_cast<int>(roundf(val));
  160. }
  161. void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
  162. {
  163. FormatInfo info;
  164. info.name = "flame";
  165. info.extension = "3dl";
  166. info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE);
  167. formatInfoVec.push_back(info);
  168. FormatInfo info2 = info;
  169. info2.name = "lustre";
  170. formatInfoVec.push_back(info2);
  171. }
  172. // Try and load the format
  173. // Raise an exception if it can't be loaded.
  174. CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const
  175. {
  176. std::vector<int> rawshaper;
  177. std::vector<int> raw3d;
  178. // Parse the file 3d lut data to an int array
  179. {
  180. const int MAX_LINE_SIZE = 4096;
  181. char lineBuffer[MAX_LINE_SIZE];
  182. std::vector<std::string> lineParts;
  183. std::vector<int> tmpData;
  184. while(istream.good())
  185. {
  186. istream.getline(lineBuffer, MAX_LINE_SIZE);
  187. // Strip and split the line
  188. pystring::split(pystring::strip(lineBuffer), lineParts);
  189. if(lineParts.empty()) continue;
  190. if((lineParts.size() > 0) && pystring::startswith(lineParts[0],"#")) continue;
  191. // If we havent found a list of ints, continue
  192. if(!StringVecToIntVec(tmpData, lineParts)) continue;
  193. // If we've found more than 3 ints, and dont have
  194. // a shaper lut yet, we've got it!
  195. if(tmpData.size()>3 && rawshaper.empty())
  196. {
  197. for(unsigned int i=0; i<tmpData.size(); ++i)
  198. {
  199. rawshaper.push_back(tmpData[i]);
  200. }
  201. }
  202. // If we've found 3 ints, add it to our 3dlut.
  203. if(tmpData.size() == 3)
  204. {
  205. raw3d.push_back(tmpData[0]);
  206. raw3d.push_back(tmpData[1]);
  207. raw3d.push_back(tmpData[2]);
  208. }
  209. }
  210. }
  211. if(raw3d.empty() && rawshaper.empty())
  212. {
  213. std::ostringstream os;
  214. os << "Error parsing .3dl file.";
  215. os << "Does not appear to contain a valid shaper lut or a 3D lut.";
  216. throw Exception(os.str().c_str());
  217. }
  218. LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile());
  219. // If all we're doing to parse the format is to read in sets of 3 numbers,
  220. // it's possible that other formats will accidentally be able to be read
  221. // mistakenly as .3dl files. We can exclude a huge segement of these mis-reads
  222. // by screening for files that use float represenations. I.e., if the MAX
  223. // value of the lut is a small number (such as <128.0) it's likely not an integer
  224. // format, and thus not a likely 3DL file.
  225. const int FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT = 128;
  226. // Interpret the shaper lut
  227. if(!rawshaper.empty())
  228. {
  229. cachedFile->has1D = true;
  230. // Find the maximum shaper lut value to infer bit-depth
  231. int shapermax = 0;
  232. for(unsigned int i=0; i<rawshaper.size(); ++i)
  233. {
  234. shapermax = std::max(shapermax, rawshaper[i]);
  235. }
  236. if(shapermax<FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT)
  237. {
  238. std::ostringstream os;
  239. os << "Error parsing .3dl file.";
  240. os << "The maximum shaper lut value, " << shapermax;
  241. os << ", is unreasonably low. This lut is probably not a .3dl ";
  242. os << "file, but instead a related format that shares a similar ";
  243. os << "structure.";
  244. throw Exception(os.str().c_str());
  245. }
  246. int shaperbitdepth = GetLikelyLutBitDepth(shapermax);
  247. if(shaperbitdepth<0)
  248. {
  249. std::ostringstream os;
  250. os << "Error parsing .3dl file.";
  251. os << "The maximum shaper lut value, " << shapermax;
  252. os << ", does not correspond to any likely bit depth. ";
  253. os << "Please confirm source file is valid.";
  254. throw Exception(os.str().c_str());
  255. }
  256. int bitdepthmax = GetMaxValueFromIntegerBitDepth(shaperbitdepth);
  257. float scale = 1.0f / static_cast<float>(bitdepthmax);
  258. for(int channel=0; channel<3; ++channel)
  259. {
  260. cachedFile->lut1D->luts[channel].resize(rawshaper.size());
  261. for(unsigned int i=0; i<rawshaper.size(); ++i)
  262. {
  263. cachedFile->lut1D->luts[channel][i] = static_cast<float>(rawshaper[i])*scale;
  264. }
  265. }
  266. // The error threshold will be 2 code values. This will allow
  267. // shaper luts which use different int conversions (round vs. floor)
  268. // to both be optimized.
  269. // Required: Abs Tolerance
  270. const int FORMAT3DL_SHAPER_CODEVALUE_TOLERANCE = 2;
  271. cachedFile->lut1D->maxerror = FORMAT3DL_SHAPER_CODEVALUE_TOLERANCE*scale;
  272. cachedFile->lut1D->errortype = ERROR_ABSOLUTE;
  273. }
  274. // Interpret the parsed data.
  275. if(!raw3d.empty())
  276. {
  277. cachedFile->has3D = true;
  278. // Find the maximum shaper lut value to infer bit-depth
  279. int lut3dmax = 0;
  280. for(unsigned int i=0; i<raw3d.size(); ++i)
  281. {
  282. lut3dmax = std::max(lut3dmax, raw3d[i]);
  283. }
  284. if(lut3dmax<FORMAT3DL_CODEVALUE_LOWEST_PLAUSIBLE_MAXINT)
  285. {
  286. std::ostringstream os;
  287. os << "Error parsing .3dl file.";
  288. os << "The maximum 3d lut value, " << lut3dmax;
  289. os << ", is unreasonably low. This lut is probably not a .3dl ";
  290. os << "file, but instead a related format that shares a similar ";
  291. os << "structure.";
  292. throw Exception(os.str().c_str());
  293. }
  294. int lut3dbitdepth = GetLikelyLutBitDepth(lut3dmax);
  295. if(lut3dbitdepth<0)
  296. {
  297. std::ostringstream os;
  298. os << "Error parsing .3dl file.";
  299. os << "The maximum 3d lut value, " << lut3dmax;
  300. os << ", does not correspond to any likely bit depth. ";
  301. os << "Please confirm source file is valid.";
  302. throw Exception(os.str().c_str());
  303. }
  304. int bitdepthmax = GetMaxValueFromIntegerBitDepth(lut3dbitdepth);
  305. float scale = 1.0f / static_cast<float>(bitdepthmax);
  306. // Interpret the int array as a 3dlut
  307. int lutEdgeLen = Get3DLutEdgeLenFromNumPixels((int)raw3d.size()/3);
  308. // Reformat 3D data
  309. cachedFile->lut3D->size[0] = lutEdgeLen;
  310. cachedFile->lut3D->size[1] = lutEdgeLen;
  311. cachedFile->lut3D->size[2] = lutEdgeLen;
  312. cachedFile->lut3D->lut.reserve(lutEdgeLen * lutEdgeLen * lutEdgeLen * 3);
  313. for(int rIndex=0; rIndex<lutEdgeLen; ++rIndex)
  314. {
  315. for(int gIndex=0; gIndex<lutEdgeLen; ++gIndex)
  316. {
  317. for(int bIndex=0; bIndex<lutEdgeLen; ++bIndex)
  318. {
  319. int i = GetLut3DIndex_B(rIndex, gIndex, bIndex,
  320. lutEdgeLen, lutEdgeLen, lutEdgeLen);
  321. cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+0]) * scale);
  322. cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+1]) * scale);
  323. cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+2]) * scale);
  324. }
  325. }
  326. }
  327. }
  328. return cachedFile;
  329. }
  330. // 65 -> 6
  331. // 33 -> 5
  332. // 17 -> 4
  333. int CubeDimensionLenToLustreBitDepth(int size)
  334. {
  335. float logval = logf(static_cast<float>(size-1)) / logf(2.0);
  336. return static_cast<int>(logval);
  337. }
  338. void LocalFileFormat::Write(const Baker & baker,
  339. const std::string & formatName,
  340. std::ostream & ostream) const
  341. {
  342. int DEFAULT_CUBE_SIZE = 0;
  343. int SHAPER_BIT_DEPTH = 10;
  344. int CUBE_BIT_DEPTH = 12;
  345. if(formatName == "lustre")
  346. {
  347. DEFAULT_CUBE_SIZE = 33;
  348. }
  349. else if(formatName == "flame")
  350. {
  351. DEFAULT_CUBE_SIZE = 17;
  352. }
  353. else
  354. {
  355. std::ostringstream os;
  356. os << "Unknown 3dl format name, '";
  357. os << formatName << "'.";
  358. throw Exception(os.str().c_str());
  359. }
  360. ConstConfigRcPtr config = baker.getConfig();
  361. int cubeSize = baker.getCubeSize();
  362. if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE;
  363. cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2
  364. int shaperSize = baker.getShaperSize();
  365. if(shaperSize==-1) shaperSize = cubeSize;
  366. std::vector<float> cubeData;
  367. cubeData.resize(cubeSize*cubeSize*cubeSize*3);
  368. GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_BLUE);
  369. PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
  370. // Apply our conversion from the input space to the output space.
  371. ConstProcessorRcPtr inputToTarget;
  372. std::string looks = baker.getLooks();
  373. if (!looks.empty())
  374. {
  375. LookTransformRcPtr transform = LookTransform::Create();
  376. transform->setLooks(looks.c_str());
  377. transform->setSrc(baker.getInputSpace());
  378. transform->setDst(baker.getTargetSpace());
  379. inputToTarget = config->getProcessor(transform,
  380. TRANSFORM_DIR_FORWARD);
  381. }
  382. else
  383. {
  384. inputToTarget = config->getProcessor(baker.getInputSpace(),
  385. baker.getTargetSpace());
  386. }
  387. inputToTarget->apply(cubeImg);
  388. // Write out the file.
  389. // For for maximum compatibility with other apps, we will
  390. // not utilize the shaper or output any metadata
  391. if(formatName == "lustre")
  392. {
  393. int meshInputBitDepth = CubeDimensionLenToLustreBitDepth(cubeSize);
  394. ostream << "3DMESH\n";
  395. ostream << "Mesh " << meshInputBitDepth << " " << CUBE_BIT_DEPTH << "\n";
  396. }
  397. std::vector<float> shaperData(shaperSize);
  398. GenerateIdentityLut1D(&shaperData[0], shaperSize, 1);
  399. float shaperScale = static_cast<float>(
  400. GetMaxValueFromIntegerBitDepth(SHAPER_BIT_DEPTH));
  401. for(unsigned int i=0; i<shaperData.size(); ++i)
  402. {
  403. if(i != 0) ostream << " ";
  404. int val = GetClampedIntFromNormFloat(shaperData[i], shaperScale);
  405. ostream << val;
  406. }
  407. ostream << "\n";
  408. // Write out the 3D Cube
  409. float cubeScale = static_cast<float>(
  410. GetMaxValueFromIntegerBitDepth(CUBE_BIT_DEPTH));
  411. if(cubeSize < 2)
  412. {
  413. throw Exception("Internal cube size exception.");
  414. }
  415. for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i)
  416. {
  417. int r = GetClampedIntFromNormFloat(cubeData[3*i+0], cubeScale);
  418. int g = GetClampedIntFromNormFloat(cubeData[3*i+1], cubeScale);
  419. int b = GetClampedIntFromNormFloat(cubeData[3*i+2], cubeScale);
  420. ostream << r << " " << g << " " << b << "\n";
  421. }
  422. ostream << "\n";
  423. if(formatName == "lustre")
  424. {
  425. ostream << "LUT8\n";
  426. ostream << "gamma 1.0\n";
  427. }
  428. }
  429. void
  430. LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
  431. const Config& /*config*/,
  432. const ConstContextRcPtr & /*context*/,
  433. CachedFileRcPtr untypedCachedFile,
  434. const FileTransform& fileTransform,
  435. TransformDirection dir) const
  436. {
  437. LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile);
  438. // This should never happen.
  439. if(!cachedFile)
  440. {
  441. std::ostringstream os;
  442. os << "Cannot build .3dl Op. Invalid cache type.";
  443. throw Exception(os.str().c_str());
  444. }
  445. TransformDirection newDir = CombineTransformDirections(dir,
  446. fileTransform.getDirection());
  447. if(newDir == TRANSFORM_DIR_UNKNOWN)
  448. {
  449. std::ostringstream os;
  450. os << "Cannot build file format transform,";
  451. os << " unspecified transform direction.";
  452. throw Exception(os.str().c_str());
  453. }
  454. // TODO: INTERP_LINEAR should not be hard-coded.
  455. // Instead query 'highest' interpolation?
  456. // (right now, it's linear). If cubic is added, consider
  457. // using it
  458. if(newDir == TRANSFORM_DIR_FORWARD)
  459. {
  460. if(cachedFile->has1D)
  461. {
  462. CreateLut1DOp(ops, cachedFile->lut1D,
  463. INTERP_LINEAR, newDir);
  464. }
  465. if(cachedFile->has3D)
  466. {
  467. CreateLut3DOp(ops, cachedFile->lut3D,
  468. fileTransform.getInterpolation(), newDir);
  469. }
  470. }
  471. else if(newDir == TRANSFORM_DIR_INVERSE)
  472. {
  473. if(cachedFile->has3D)
  474. {
  475. CreateLut3DOp(ops, cachedFile->lut3D,
  476. fileTransform.getInterpolation(), newDir);
  477. }
  478. if(cachedFile->has1D)
  479. {
  480. CreateLut1DOp(ops, cachedFile->lut1D,
  481. INTERP_LINEAR, newDir);
  482. }
  483. }
  484. }
  485. }
  486. FileFormat * CreateFileFormat3DL()
  487. {
  488. return new LocalFileFormat();
  489. }
  490. }
  491. OCIO_NAMESPACE_EXIT
  492. #ifdef OCIO_UNIT_TEST
  493. namespace OCIO = OCIO_NAMESPACE;
  494. #include "UnitTest.h"
  495. // FILE EXPECTED MAX CORRECTLY DECODED IF MAX IN THIS RANGE
  496. // 8-bit 255 [0, 511]
  497. // 10-bit 1023 [512, 2047]
  498. // 12-bit 4095 [2048, 8191]
  499. // 14-bit 16383 [8192, 32767]
  500. // 16-bit 65535 [32768, 131071]
  501. OIIO_ADD_TEST(FileFormat3DL, GetLikelyLutBitDepth)
  502. {
  503. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(-1), -1);
  504. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(0), 8);
  505. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1), 8);
  506. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(255), 8);
  507. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(256), 8);
  508. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(511), 8);
  509. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(512), 10);
  510. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1023), 10);
  511. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(1024), 10);
  512. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(2047), 10);
  513. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(2048), 12);
  514. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(4095), 12);
  515. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(4096), 12);
  516. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(8191), 12);
  517. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(16383), 14);
  518. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(65535), 16);
  519. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(65536), 16);
  520. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(131071), 16);
  521. OIIO_CHECK_EQUAL(OCIO::GetLikelyLutBitDepth(131072), 16);
  522. }
  523. #endif // OCIO_UNIT_TEST