PageRenderTime 165ms CodeModel.GetById 41ms app.highlight 112ms RepoModel.GetById 1ms app.codeStats 1ms

/src/core/FileFormatHDL.cpp

http://github.com/imageworks/OpenColorIO
C++ | 1481 lines | 1081 code | 204 blank | 196 comment | 148 complexity | 3a21d222f96fc2aa4a87335dbfd052f5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
   3All Rights Reserved.
   4
   5Redistribution and use in source and binary forms, with or without
   6modification, are permitted provided that the following conditions are
   7met:
   8* Redistributions of source code must retain the above copyright
   9  notice, this list of conditions and the following disclaimer.
  10* Redistributions in binary form must reproduce the above copyright
  11  notice, this list of conditions and the following disclaimer in the
  12  documentation and/or other materials provided with the distribution.
  13* Neither the name of Sony Pictures Imageworks nor the names of its
  14  contributors may be used to endorse or promote products derived from
  15  this software without specific prior written permission.
  16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27*/
  28
  29/*
  30    
  31    Houdini LUTs
  32    http://www.sidefx.com/docs/hdk11.0/hdk_io_lut.html
  33    
  34    Types:
  35      - 1D Lut (partial support)
  36      - 3D Lut
  37      - 3D Lut with 1D Prelut
  38    
  39    TODO:
  40        - Add support for other 1D types (R, G, B, A, RGB, RGBA, All)
  41          we only support type 'C' atm.
  42        - Add support for 'Sampling' tag
  43    
  44*/
  45
  46#include <cstdio>
  47#include <iostream>
  48#include <iterator>
  49#include <cmath>
  50#include <vector>
  51#include <string>
  52#include <algorithm>
  53#include <map>
  54
  55#include <OpenColorIO/OpenColorIO.h>
  56
  57#include "FileTransform.h"
  58#include "Lut1DOp.h"
  59#include "Lut3DOp.h"
  60#include "ParseUtils.h"
  61#include "MathUtils.h"
  62#include "pystring/pystring.h"
  63
  64OCIO_NAMESPACE_ENTER
  65{
  66    namespace
  67    {
  68        // HDL parser helpers
  69
  70        // HDL headers/LUT's are shoved into these datatypes
  71        typedef std::map<std::string, std::vector<std::string> > StringToStringVecMap;
  72        typedef std::map<std::string, std::vector<float> > StringToFloatVecMap;
  73
  74        void
  75        readHeaders(StringToStringVecMap& headers,
  76                    std::istream& istream)
  77        {
  78            std::string line;
  79            while(nextline(istream, line))
  80            {
  81                std::vector<std::string> chunks;
  82
  83                // Remove trailing/leading whitespace, lower-case and
  84                // split into words
  85                pystring::split(pystring::lower(pystring::strip(line)), chunks);
  86
  87                // Skip empty lines
  88                if(chunks.empty()) continue;
  89
  90                // Stop looking for headers at the "LUT:" line
  91                if(chunks[0] == "lut:") break;
  92
  93                // Use first index as key, and remove it from the value
  94                std::string key = chunks[0];
  95                chunks.erase(chunks.begin());
  96
  97                headers[key] = chunks;
  98            }
  99        }
 100
 101        // Try to grab key (e.g "version") from headers. Throws
 102        // exception if not found, or if number of chunks in value is
 103        // not between min_vals and max_vals (e.g the "length" key
 104        // must exist, and must have either 1 or 2 values)
 105        std::vector<std::string>
 106        findHeaderItem(StringToStringVecMap& headers,
 107                       const std::string key,
 108                       const unsigned int min_vals,
 109                       const unsigned int max_vals)
 110        {
 111            StringToStringVecMap::iterator iter;
 112            iter = headers.find(key);
 113
 114            // Error if key is not found
 115            if(iter == headers.end())
 116            {
 117                std::ostringstream os;
 118                os << "'" << key << "' line not found";
 119                throw Exception(os.str().c_str());
 120            }
 121
 122            // Error if incorrect number of values is found
 123            if(iter->second.size() < min_vals ||
 124               iter->second.size() > max_vals)
 125            {
 126                std::ostringstream os;
 127                os << "Incorrect number of chunks (" << iter->second.size() << ")";
 128                os << " after '" << key << "' line, expected ";
 129
 130                if(min_vals == max_vals)
 131                {
 132                    os << min_vals;
 133                }
 134                else
 135                {
 136                    os << "between " << min_vals << " and " << max_vals;
 137                }
 138
 139                throw Exception(os.str().c_str());
 140            }
 141
 142            return iter->second;
 143        }
 144
 145        // Simple wrapper to call findHeaderItem with a fixed number
 146        // of values (e.g "version" should have a single value)
 147        std::vector<std::string>
 148        findHeaderItem(StringToStringVecMap& chunks,
 149                       const std::string key,
 150                       const unsigned int numvals)
 151        {
 152            return findHeaderItem(chunks, key, numvals, numvals);
 153        }
 154
 155        // Crudely parse LUT's - doesn't do any length checking etc,
 156        // just grabs a series of floats for Pre{...}, 3d{...} etc
 157        // Does some basic error checking, but there are situations
 158        // were it could incorrectly accept broken data (like
 159        // "Pre{0.0\n1.0}blah"), but hopefully none where it misses
 160        // data
 161        void
 162        readLuts(std::istream& istream,
 163                 StringToFloatVecMap& lutValues)
 164        {
 165            // State variables
 166            bool inlut = false;
 167            std::string lutname;
 168
 169            std::string word;
 170
 171            while(istream >> word)
 172            {
 173                if(!inlut)
 174                {
 175                    if(word == "{")
 176                    {
 177                        // Lone "{" is for a 3D
 178                        inlut = true;
 179                        lutname = "3d";
 180                    }
 181                    else
 182                    {
 183                        // Named lut, e.g "Pre {"
 184                        inlut = true;
 185                        lutname = pystring::lower(word);
 186
 187                        // Ensure next word is "{"
 188                        std::string nextword;
 189                        istream >> nextword;
 190                        if(nextword != "{")
 191                        {
 192                            std::ostringstream os;
 193                            os << "Malformed LUT - Unknown word '";
 194                            os << word << "' after LUT name '";
 195                            os << nextword << "'";
 196                            throw Exception(os.str().c_str());
 197                        }
 198                    }
 199                }
 200                else if(word == "}")
 201                {
 202                    // end of LUT
 203                    inlut = false;
 204                    lutname = "";
 205                }
 206                else if(inlut)
 207                {
 208                    // StringToFloat was far slower, for 787456 values:
 209                    // - StringToFloat took 3879 (avg nanoseconds per value)
 210                    // - stdtod took 169 nanoseconds
 211                    char* endptr = 0;
 212                    float v = static_cast<float>(strtod(word.c_str(), &endptr));
 213
 214                    if(!*endptr)
 215                    {
 216                        // Since each word should contain a single
 217                        // float value, the pointer should be null
 218                        lutValues[lutname].push_back(v);
 219                    }
 220                    else
 221                    {
 222                        // stdtod endptr still contained stuff,
 223                        // meaning an invalid float value
 224                        std::ostringstream os;
 225                        os << "Invalid float value in " << lutname;
 226                        os << " LUT, '" << word << "'";
 227                        throw Exception(os.str().c_str());
 228                    }
 229                }
 230                else
 231                {
 232                    std::ostringstream os;
 233                    os << "Unexpected word, possibly a value outside";
 234                    os <<" a LUT {} block. Word was '" << word << "'";
 235                    throw Exception(os.str().c_str());
 236
 237                }
 238            }
 239        }
 240
 241    } // end anonymous "HDL parser helpers" namespace
 242
 243    namespace
 244    {
 245        class CachedFileHDL : public CachedFile
 246        {
 247        public:
 248            CachedFileHDL ()
 249            {
 250                hdlversion = "unknown";
 251                hdlformat = "unknown";
 252                hdltype = "unknown";
 253                hdlblack = 0.0;
 254                hdlwhite = 1.0;
 255                lut1D = Lut1D::Create();
 256                lut3D = Lut3D::Create();
 257            };
 258            ~CachedFileHDL() {};
 259            std::string hdlversion;
 260            std::string hdlformat;
 261            std::string hdltype;
 262            float to_min; // TODO: maybe add this to Lut1DOp?
 263            float to_max; // TODO: maybe add this to Lut1DOp?
 264            float hdlblack;
 265            float hdlwhite;
 266            Lut1DRcPtr lut1D;
 267            Lut3DRcPtr lut3D;
 268        };
 269        typedef OCIO_SHARED_PTR<CachedFileHDL> CachedFileHDLRcPtr;
 270        
 271        class LocalFileFormat : public FileFormat
 272        {
 273        public:
 274            
 275            ~LocalFileFormat() {};
 276            
 277            virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const;
 278            
 279            virtual CachedFileRcPtr Read(std::istream & istream) const;
 280            
 281            virtual void Write(const Baker & baker,
 282                               const std::string & formatName,
 283                               std::ostream & ostream) const;
 284            
 285            virtual void BuildFileOps(OpRcPtrVec & ops,
 286                                      const Config& config,
 287                                      const ConstContextRcPtr & context,
 288                                      CachedFileRcPtr untypedCachedFile,
 289                                      const FileTransform& fileTransform,
 290                                      TransformDirection dir) const;
 291        };
 292        
 293        void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const
 294        {
 295            FormatInfo info;
 296            info.name = "houdini";
 297            info.extension = "lut";
 298            info.capabilities = FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE;
 299            formatInfoVec.push_back(info);
 300        }
 301        
 302        CachedFileRcPtr
 303        LocalFileFormat::Read(std::istream & istream) const
 304        {
 305            
 306            // this shouldn't happen
 307            if (!istream)
 308                throw Exception ("file stream empty when trying to read Houdini lut");
 309
 310            //
 311            CachedFileHDLRcPtr cachedFile = CachedFileHDLRcPtr (new CachedFileHDL ());
 312            Lut1DRcPtr lut1d_ptr = Lut1D::Create();
 313            Lut3DRcPtr lut3d_ptr = Lut3D::Create();
 314
 315            // Parse headers into key-value pairs
 316            StringToStringVecMap header_chunks;
 317            StringToStringVecMap::iterator iter;
 318
 319            // Read headers, ending after the "LUT:" line
 320            readHeaders(header_chunks, istream);
 321
 322            // Grab useful values from headers
 323            std::vector<std::string> value;
 324
 325            // "Version 3" - format version (currently one version
 326            // number per LUT type)
 327            value = findHeaderItem(header_chunks, "version", 1);
 328            cachedFile->hdlversion = value[0];
 329
 330            // "Format any" - bit depth of image the LUT should be
 331            // applied to (this is basically ignored)
 332            value = findHeaderItem(header_chunks, "format", 1);
 333            cachedFile->hdlformat = value[0];
 334
 335            // "Type 3d" - type of LUT
 336            {
 337                value = findHeaderItem(header_chunks, "type", 1);
 338
 339                cachedFile->hdltype = value[0];
 340            }
 341
 342            // "From 0.0 1.0" - range of input values
 343            {
 344                float from_min, from_max;
 345
 346                value = findHeaderItem(header_chunks, "from", 2);
 347
 348                if(!StringToFloat(&from_min, value[0].c_str()) ||
 349                   !StringToFloat(&from_max, value[1].c_str()))
 350                {
 351                    std::ostringstream os;
 352                    os << "Invalid float value(s) on 'From' line, '";
 353                    os << value[0] << "' and '"  << value[1] << "'";
 354                    throw Exception(os.str().c_str());
 355                }
 356
 357                for(int i = 0; i < 3; ++i)
 358                {
 359                    lut1d_ptr->from_min[i] = from_min;
 360                    lut1d_ptr->from_max[i] = from_max;
 361                }
 362            }
 363
 364
 365            // "To 0.0 1.0" - range of values in LUT (e.g "0 255"
 366            // to specify values as 8-bit numbers, usually "0 1")
 367            {
 368                float to_min, to_max;
 369
 370                value = findHeaderItem(header_chunks, "to", 2);
 371
 372                if(!StringToFloat(&to_min, value[0].c_str()) ||
 373                   !StringToFloat(&to_max, value[1].c_str()))
 374                {
 375                    std::ostringstream os;
 376                    os << "Invalid float value(s) on 'To' line, '";
 377                    os << value[0] << "' and '"  << value[1] << "'";
 378                    throw Exception(os.str().c_str());
 379                }
 380                cachedFile->to_min = to_min;
 381                cachedFile->to_max = to_max;
 382            }
 383
 384            // "Black 0" and "White 1" - obsolete options, should be 0
 385            // and 1
 386
 387            {
 388                value = findHeaderItem(header_chunks, "black", 1);
 389
 390                float black;
 391
 392                if(!StringToFloat(&black, value[0].c_str()))
 393                {
 394                    std::ostringstream os;
 395                    os << "Invalid float value on 'Black' line, '";
 396                    os << value[0] << "'";
 397                    throw Exception(os.str().c_str());
 398                }
 399                cachedFile->hdlblack = black;
 400            }
 401
 402            {
 403                value = findHeaderItem(header_chunks, "white", 1);
 404
 405                float white;
 406
 407                if(!StringToFloat(&white, value[0].c_str()))
 408                {
 409                    std::ostringstream os;
 410                    os << "Invalid float value on 'White' line, '";
 411                    os << value[0] << "'";
 412                    throw Exception(os.str().c_str());
 413                }
 414                cachedFile->hdlwhite = white;
 415            }
 416
 417
 418            // Verify type is valid and supported - used to handle
 419            // length sensibly, and checking the LUT later
 420            {
 421                std::string ltype = cachedFile->hdltype;
 422                if(ltype != "3d" && ltype != "3d+1d" && ltype != "c")
 423                {
 424                    std::ostringstream os;
 425                    os << "Unsupported Houdini LUT type: '" << ltype << "'";
 426                    throw Exception(os.str().c_str());
 427                }
 428            }
 429
 430
 431            // "Length 2" or "Length 2 5" - either "[cube size]", or "[cube
 432            // size] [prelut size]"
 433            int size_3d = -1;
 434            int size_prelut = -1;
 435            int size_1d = -1;
 436
 437            {
 438                std::vector<int> lut_sizes;
 439
 440                value = findHeaderItem(header_chunks, "length", 1, 2);
 441                for(unsigned int i = 0; i < value.size(); ++i)
 442                {
 443                    int tmpsize = -1;
 444                    if(!StringToInt(&tmpsize, value[i].c_str()))
 445                    {
 446                        std::ostringstream os;
 447                        os << "Invalid integer on 'Length' line: ";
 448                        os << "'" << value[0] << "'";
 449                        throw Exception(os.str().c_str());
 450                    }
 451                    lut_sizes.push_back(tmpsize);
 452                }
 453
 454                if(cachedFile->hdltype == "3d" || cachedFile->hdltype == "3d+1d")
 455                {
 456                    // Set cube size
 457                    size_3d = lut_sizes[0];
 458
 459                    lut3d_ptr->size[0] = lut_sizes[0];
 460                    lut3d_ptr->size[1] = lut_sizes[0];
 461                    lut3d_ptr->size[2] = lut_sizes[0];
 462                }
 463
 464                if(cachedFile->hdltype == "c")
 465                {
 466                    size_1d = lut_sizes[0];
 467                }
 468
 469                if(cachedFile->hdltype == "3d+1d")
 470                {
 471                    size_prelut = lut_sizes[1];
 472                }
 473            }
 474
 475            // Read stuff after "LUT:"
 476            StringToFloatVecMap lut_data;
 477            readLuts(istream, lut_data);
 478
 479            //
 480            StringToFloatVecMap::iterator lut_iter;
 481
 482            if(cachedFile->hdltype == "3d+1d")
 483            {
 484                // Read prelut, and bind onto cachedFile
 485                lut_iter = lut_data.find("pre");
 486                if(lut_iter == lut_data.end())
 487                {
 488                    std::ostringstream os;
 489                    os << "3D+1D LUT should contain Pre{} LUT section";
 490                    throw Exception(os.str().c_str());
 491                }
 492
 493                if(size_prelut != static_cast<int>(lut_iter->second.size()))
 494                {
 495                    std::ostringstream os;
 496                    os << "Pre{} LUT was " << lut_iter->second.size();
 497                    os << " values long, expected " << size_prelut << " values";
 498                    throw Exception(os.str().c_str());
 499                }
 500
 501                lut1d_ptr->luts[0] = lut_iter->second;
 502                lut1d_ptr->luts[1] = lut_iter->second;
 503                lut1d_ptr->luts[2] = lut_iter->second;
 504                lut1d_ptr->maxerror = 0.0f;
 505                lut1d_ptr->errortype = ERROR_RELATIVE;
 506                cachedFile->lut1D = lut1d_ptr;
 507            }
 508
 509            if(cachedFile->hdltype == "3d" ||
 510               cachedFile->hdltype == "3d+1d")
 511            {
 512                // Bind 3D LUT to lut3d_ptr, along with some
 513                // slightly-elabourate error messages
 514
 515                lut_iter = lut_data.find("3d");
 516                if(lut_iter == lut_data.end())
 517                {
 518                    std::ostringstream os;
 519                    os << "3D LUT section not found";
 520                    throw Exception(os.str().c_str());
 521                }
 522
 523                int size_3d_cubed = size_3d * size_3d * size_3d;
 524
 525                if(size_3d_cubed * 3 != static_cast<int>(lut_iter->second.size()))
 526                {
 527                    int foundsize = static_cast<int>(lut_iter->second.size());
 528                    int foundlines = foundsize / 3;
 529
 530                    std::ostringstream os;
 531                    os << "3D LUT contains incorrect number of values. ";
 532                    os << "Contained " << foundsize << " values ";
 533                    os << "(" << foundlines << " lines), ";
 534                    os << "expected " << (size_3d_cubed*3) << " values ";
 535                    os << "(" << size_3d_cubed << " lines)";
 536                    throw Exception(os.str().c_str());
 537                }
 538
 539                lut3d_ptr->lut = lut_iter->second;
 540
 541                // Bind to cachedFile
 542                cachedFile->lut3D = lut3d_ptr;
 543            }
 544
 545            if(cachedFile->hdltype == "c")
 546            {
 547                // Bind simple 1D RGB LUT
 548                lut_iter = lut_data.find("rgb");
 549                if(lut_iter == lut_data.end())
 550                {
 551                    std::ostringstream os;
 552                    os << "3D+1D LUT should contain Pre{} LUT section";
 553                    throw Exception(os.str().c_str());
 554                }
 555
 556                if(size_1d != static_cast<int>(lut_iter->second.size()))
 557                {
 558                    std::ostringstream os;
 559                    os << "RGB{} LUT was " << lut_iter->second.size();
 560                    os << " values long, expected " << size_1d << " values";
 561                    throw Exception(os.str().c_str());
 562                }
 563
 564                lut1d_ptr->luts[0] = lut_iter->second;
 565                lut1d_ptr->luts[1] = lut_iter->second;
 566                lut1d_ptr->luts[2] = lut_iter->second;
 567                lut1d_ptr->maxerror = 0.0f;
 568                lut1d_ptr->errortype = ERROR_RELATIVE;
 569                cachedFile->lut1D = lut1d_ptr;
 570            }
 571
 572            return cachedFile;
 573        }
 574        
 575        void LocalFileFormat::Write(const Baker & baker,
 576                                    const std::string & formatName,
 577                                    std::ostream & ostream) const
 578        {
 579
 580            if(formatName != "houdini")
 581            {
 582                std::ostringstream os;
 583                os << "Unknown hdl format name, '";
 584                os << formatName << "'.";
 585                throw Exception(os.str().c_str());
 586            }
 587
 588            // Get config
 589            ConstConfigRcPtr config = baker.getConfig();
 590
 591            // setup the floating point precision
 592            ostream.setf(std::ios::fixed, std::ios::floatfield);
 593            ostream.precision(6);
 594
 595            // Default sizes
 596            const int DEFAULT_SHAPER_SIZE = 1024;
 597            // MPlay produces bad results with 32^3 cube (in a way
 598            // that looks more quantised than even "nearest"
 599            // interpolation in OCIOFileTransform)
 600            const int DEFAULT_CUBE_SIZE = 64;
 601            const int DEFAULT_1D_SIZE = 1024;
 602
 603            // Get configured sizes
 604            int cubeSize = baker.getCubeSize();
 605            int shaperSize = baker.getShaperSize();
 606            // FIXME: Misusing cube size to set 1D LUT size, as it seemed
 607            // slightly less confusing than using the shaper LUT size
 608            int onedSize = baker.getCubeSize();
 609            
 610            // Defaults and sanity check on cube size
 611            if(cubeSize == -1) cubeSize = DEFAULT_CUBE_SIZE;
 612            if(cubeSize < 0) cubeSize = DEFAULT_CUBE_SIZE;
 613            if(cubeSize<2)
 614            {
 615                std::ostringstream os;
 616                os << "Cube size must be 2 or larger (was " << cubeSize << ")";
 617                throw Exception(os.str().c_str());
 618            }
 619            
 620            // ..and same for shaper size
 621            if(shaperSize<0) shaperSize = DEFAULT_SHAPER_SIZE;
 622            if(shaperSize<2)
 623            {
 624                std::ostringstream os;
 625                os << "A shaper space ('" << baker.getShaperSpace() << "') has";
 626                os << " been specified, so the shaper size must be 2 or larger";
 627                throw Exception(os.str().c_str());
 628            }
 629            
 630            // ..and finally, for the 1D LUT size
 631            if(onedSize == -1) onedSize = DEFAULT_1D_SIZE;
 632            if(onedSize < 2)
 633            {
 634                std::ostringstream os;
 635                os << "1D LUT size must be higher than 2 (was " << onedSize << ")";
 636                throw Exception(os.str().c_str());
 637            }
 638            
 639            // Version numbers
 640            const int HDL_1D = 1; // 1D LUT version number
 641            const int HDL_3D = 2; // 3D LUT version number
 642            const int HDL_3D1D = 3; // 3D LUT with 1D prelut
 643            
 644            // Get spaces from baker
 645            const std::string shaperSpace = baker.getShaperSpace();
 646            const std::string inputSpace = baker.getInputSpace();
 647            const std::string targetSpace = baker.getTargetSpace();
 648            const std::string looks = baker.getLooks();
 649
 650            // Determine required LUT type
 651            ConstProcessorRcPtr inputToTargetProc;
 652            if (!looks.empty())
 653            {
 654                LookTransformRcPtr transform = LookTransform::Create();
 655                transform->setLooks(looks.c_str());
 656                transform->setSrc(inputSpace.c_str());
 657                transform->setDst(targetSpace.c_str());
 658                inputToTargetProc = config->getProcessor(transform,
 659                    TRANSFORM_DIR_FORWARD);
 660            }
 661            else
 662            {
 663                inputToTargetProc = config->getProcessor(
 664                    inputSpace.c_str(),
 665                    targetSpace.c_str());
 666            }
 667
 668            int required_lut = -1;
 669            
 670            if(inputToTargetProc->hasChannelCrosstalk())
 671            {
 672                if(shaperSpace.empty())
 673                {
 674                    // Has crosstalk, but no prelut, so need 3D LUT
 675                    required_lut = HDL_3D;
 676                }
 677                else
 678                {
 679                    // Crosstalk with shaper-space
 680                    required_lut = HDL_3D1D;
 681                }
 682            }
 683            else
 684            {
 685                // No crosstalk
 686                required_lut = HDL_1D;
 687            }
 688
 689            if(required_lut == -1)
 690            {
 691                // Unnecessary paranoia
 692                throw Exception(
 693                    "Internal logic error, LUT type was not determined");
 694            }
 695            
 696            // Make prelut
 697            std::vector<float> prelutData;
 698            
 699            float fromInStart = 0; // for "From:" part of header
 700            float fromInEnd = 1;
 701            
 702            if(required_lut == HDL_3D1D)
 703            {
 704                // TODO: Later we only grab the green channel for the prelut,
 705                // should ensure the prelut is monochromatic somehow?
 706                
 707                ConstProcessorRcPtr inputToShaperProc = config->getProcessor(
 708                    inputSpace.c_str(),
 709                    shaperSpace.c_str());
 710                
 711                if(inputToShaperProc->hasChannelCrosstalk())
 712                {
 713                    // TODO: Automatically turn shaper into
 714                    // non-crosstalked version?
 715                    std::ostringstream os;
 716                    os << "The specified shaperSpace, '" << baker.getShaperSpace();
 717                    os << "' has channel crosstalk, which is not appropriate for";
 718                    os << " shapers. Please select an alternate shaper space or";
 719                    os << " omit this option.";
 720                    throw Exception(os.str().c_str());
 721                }
 722                
 723                // Calculate min/max value
 724                {
 725                    // Get input value of 1.0 in shaper space, as this
 726                    // is the higest value that is transformed by the
 727                    // cube (e.g for a generic lin-to-log trasnform,
 728                    // what the log value 1.0 is in linear).
 729                    ConstProcessorRcPtr shaperToInputProc = config->getProcessor(
 730                        shaperSpace.c_str(),
 731                        inputSpace.c_str());
 732
 733                    float minval[3] = {0.0f, 0.0f, 0.0f};
 734                    float maxval[3] = {1.0f, 1.0f, 1.0f};
 735
 736                    shaperToInputProc->applyRGB(minval);
 737                    shaperToInputProc->applyRGB(maxval);
 738                    
 739                    // Grab green channel, as this is the one used later
 740                    fromInStart = minval[1];
 741                    fromInEnd = maxval[1];
 742                }
 743
 744                // Generate the identity prelut values, then apply the transform.
 745                // Prelut is linearly sampled from fromInStart to fromInEnd
 746                prelutData.resize(shaperSize*3);
 747                
 748                for (int i = 0; i < shaperSize; ++i)
 749                {
 750                    const float x = (float)(double(i) / double(shaperSize - 1));
 751                    float cur_value = lerpf(fromInStart, fromInEnd, x);
 752
 753                    prelutData[3*i+0] = cur_value;
 754                    prelutData[3*i+1] = cur_value;
 755                    prelutData[3*i+2] = cur_value;
 756                }
 757                
 758                PackedImageDesc prelutImg(&prelutData[0], shaperSize, 1, 3);
 759                inputToShaperProc->apply(prelutImg);
 760            }
 761            
 762            // TODO: Do same "auto prelut" input-space allocation as FileFormatCSP?
 763            
 764            // Make 3D LUT
 765            std::vector<float> cubeData;
 766            if(required_lut == HDL_3D || required_lut == HDL_3D1D)
 767            {
 768                cubeData.resize(cubeSize*cubeSize*cubeSize*3);
 769                
 770                GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED);
 771                PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3);
 772                
 773                ConstProcessorRcPtr cubeProc;
 774                if(required_lut == HDL_3D1D)
 775                {
 776                    // Prelut goes from input-to-shaper, so cube goes from shaper-to-target
 777                    if (!looks.empty())
 778                    {
 779                        LookTransformRcPtr transform = LookTransform::Create();
 780                        transform->setLooks(looks.c_str());
 781                        transform->setSrc(inputSpace.c_str());
 782                        transform->setDst(targetSpace.c_str());
 783                        cubeProc = config->getProcessor(transform,
 784                            TRANSFORM_DIR_FORWARD);
 785                    }
 786                    else
 787                    {
 788                        cubeProc = config->getProcessor(shaperSpace.c_str(),
 789                                                        targetSpace.c_str());
 790                    }
 791                }
 792                else
 793                {
 794                    // No prelut, so cube goes from input-to-target
 795                  cubeProc = inputToTargetProc;
 796                }
 797
 798                
 799                cubeProc->apply(cubeImg);
 800            }
 801            
 802            
 803            // Make 1D LUT
 804            std::vector<float> onedData;
 805            if(required_lut == HDL_1D)
 806            {
 807                onedData.resize(onedSize * 3);
 808                
 809                GenerateIdentityLut1D(&onedData[0], onedSize, 3);
 810                PackedImageDesc onedImg(&onedData[0], onedSize, 1, 3);
 811                
 812                inputToTargetProc->apply(onedImg);
 813            }
 814            
 815            
 816            // Write the file contents
 817            ostream << "Version\t\t" << required_lut << "\n";
 818            ostream << "Format\t\t" << "any" << "\n";
 819            
 820            ostream << "Type\t\t";
 821            if(required_lut == HDL_1D)
 822                ostream << "RGB";
 823            if(required_lut == HDL_3D)
 824                ostream << "3D";
 825            if(required_lut == HDL_3D1D)
 826                ostream << "3D+1D";
 827            ostream << "\n";
 828            
 829            ostream << "From\t\t" << fromInStart << " " << fromInEnd << "\n";
 830            ostream << "To\t\t" << 0.0f << " " << 1.0f << "\n";
 831            ostream << "Black\t\t" << 0.0f << "\n";
 832            ostream << "White\t\t" << 1.0f << "\n";
 833            
 834            if(required_lut == HDL_3D1D)
 835                ostream << "Length\t\t" << cubeSize << " " << shaperSize << "\n";
 836            if(required_lut == HDL_3D)
 837                ostream << "Length\t\t" << cubeSize << "\n";
 838            if(required_lut == HDL_1D)
 839                ostream << "Length\t\t" << onedSize << "\n";
 840            
 841            ostream << "LUT:\n";
 842            
 843            // Write prelut
 844            if(required_lut == HDL_3D1D)
 845            {
 846                ostream << "Pre {\n";
 847                for(int i=0; i < shaperSize; ++i)
 848                {
 849                    // Grab green channel from RGB prelut
 850                    ostream << "\t" << prelutData[i*3+1] << "\n";
 851                }
 852                ostream << "}\n";
 853            }
 854            
 855            // Write "3D {" part of output of 3D+1D LUT
 856            if(required_lut == HDL_3D1D)
 857            {
 858                ostream << "3D {\n";
 859            }
 860            
 861            // Write the slightly-different "{" without line for the 3D-only LUT
 862            if(required_lut == HDL_3D)
 863            {
 864                ostream << " {\n";
 865            }
 866            
 867            // Write the cube data after the "{"
 868            if(required_lut == HDL_3D || required_lut == HDL_3D1D)
 869            {
 870                for(int i=0; i < cubeSize*cubeSize*cubeSize; ++i)
 871                {
 872                    // TODO: Original baker code clamped values to
 873                    // 1.0, was this necessary/desirable?
 874
 875                    ostream << "\t" << cubeData[3*i+0];
 876                    ostream << " "  << cubeData[3*i+1];
 877                    ostream << " "  << cubeData[3*i+2] << "\n";
 878                }
 879                
 880                // Write closing "}"
 881                ostream << " }\n";
 882            }
 883            
 884            // Write out channels for 1D LUT
 885            if(required_lut == HDL_1D)
 886            {
 887                ostream << "R {\n";
 888                for(int i=0; i < onedSize; ++i)
 889                    ostream << "\t" << onedData[i*3+0] << "\n";
 890                ostream << "}\n";
 891
 892                ostream << "G {\n";
 893                for(int i=0; i < onedSize; ++i)
 894                    ostream << "\t" << onedData[i*3+1] << "\n";
 895                ostream << "}\n";
 896
 897                ostream << "B {\n";
 898                for(int i=0; i < onedSize; ++i)
 899                    ostream << "\t" << onedData[i*3+2] << "\n";
 900                ostream << "}\n";
 901            }
 902        }
 903        
 904        void
 905        LocalFileFormat::BuildFileOps(OpRcPtrVec & ops,
 906                                    const Config& /*config*/,
 907                                    const ConstContextRcPtr & /*context*/,
 908                                    CachedFileRcPtr untypedCachedFile,
 909                                    const FileTransform& fileTransform,
 910                                    TransformDirection dir) const
 911        {
 912            
 913            CachedFileHDLRcPtr cachedFile = DynamicPtrCast<CachedFileHDL>(untypedCachedFile);
 914            
 915            // This should never happen.
 916            if(!cachedFile)
 917            {
 918                std::ostringstream os;
 919                os << "Cannot build Houdini Op. Invalid cache type.";
 920                throw Exception(os.str().c_str());
 921            }
 922            
 923            TransformDirection newDir = CombineTransformDirections(dir,
 924                fileTransform.getDirection());
 925            
 926            if(newDir == TRANSFORM_DIR_FORWARD) {
 927                if(cachedFile->hdltype == "c")
 928                {
 929                    CreateLut1DOp(ops, cachedFile->lut1D,
 930                                  fileTransform.getInterpolation(), newDir);
 931                }
 932                else if(cachedFile->hdltype == "3d")
 933                {
 934                    CreateLut3DOp(ops, cachedFile->lut3D,
 935                                  fileTransform.getInterpolation(), newDir);
 936                }
 937                else if(cachedFile->hdltype == "3d+1d")
 938                {
 939                    CreateLut1DOp(ops, cachedFile->lut1D,
 940                                  fileTransform.getInterpolation(), newDir);
 941                    CreateLut3DOp(ops, cachedFile->lut3D,
 942                                  fileTransform.getInterpolation(), newDir);
 943                }
 944                else
 945                {
 946                    throw Exception("Unhandled hdltype while creating forward ops");
 947                }
 948            } else if(newDir == TRANSFORM_DIR_INVERSE) {
 949                if(cachedFile->hdltype == "c")
 950                {
 951                    CreateLut1DOp(ops, cachedFile->lut1D,
 952                                  fileTransform.getInterpolation(), newDir);
 953                }
 954                else if(cachedFile->hdltype == "3d")
 955                {
 956                    CreateLut3DOp(ops, cachedFile->lut3D,
 957                                  fileTransform.getInterpolation(), newDir);
 958                }
 959                else if(cachedFile->hdltype == "3d+1d")
 960                {
 961                    CreateLut3DOp(ops, cachedFile->lut3D,
 962                                  fileTransform.getInterpolation(), newDir);
 963                    CreateLut1DOp(ops, cachedFile->lut1D,
 964                                  fileTransform.getInterpolation(), newDir);
 965                }
 966                else
 967                {
 968                    throw Exception("Unhandled hdltype while creating reverse ops");
 969                }
 970            }
 971            return;
 972        }
 973    }
 974    
 975    FileFormat * CreateFileFormatHDL()
 976    {
 977        return new LocalFileFormat();
 978    }
 979}
 980OCIO_NAMESPACE_EXIT
 981
 982///////////////////////////////////////////////////////////////////////////////
 983
 984#ifdef OCIO_UNIT_TEST
 985
 986namespace OCIO = OCIO_NAMESPACE;
 987#include "UnitTest.h"
 988
 989OIIO_ADD_TEST(FileFormatHDL, Read1D)
 990{
 991    std::ostringstream strebuf;
 992    strebuf << "Version\t\t1" << "\n";
 993    strebuf << "Format\t\tany" << "\n";
 994    strebuf << "Type\t\tC" << "\n";
 995    strebuf << "From\t\t0.1 3.2" << "\n";
 996    strebuf << "To\t\t0 1" << "\n";
 997    strebuf << "Black\t\t0" << "\n";
 998    strebuf << "White\t\t0.99" << "\n";
 999    strebuf << "Length\t\t9" << "\n";
1000    strebuf << "LUT:" << "\n";
1001    strebuf << "RGB {" << "\n";
1002    strebuf << "\t0" << "\n";
1003    strebuf << "\t0.000977517" << "\n";
1004    strebuf << "\t0.00195503" << "\n";
1005    strebuf << "\t0.00293255" << "\n";
1006    strebuf << "\t0.00391007" << "\n";
1007    strebuf << "\t0.00488759" << "\n";
1008    strebuf << "\t0.0058651" << "\n";
1009    strebuf << "\t0.999022" << "\n";
1010    strebuf << "\t1.67 }" << "\n";
1011    
1012    //
1013    float from_min = 0.1f;
1014    float from_max = 3.2f;
1015    float to_min = 0.0f;
1016    float to_max = 1.0f;
1017    float black = 0.0f;
1018    float white = 0.99f;
1019    float lut1d[9] = { 0.0f, 0.000977517f, 0.00195503f, 0.00293255f,
1020        0.00391007f, 0.00488759f, 0.0058651f, 0.999022f, 1.67f };
1021    
1022    std::istringstream simple3D1D;
1023    simple3D1D.str(strebuf.str());
1024    
1025    // Load file
1026    OCIO::LocalFileFormat tester;
1027    OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
1028    OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
1029    
1030    //
1031    OIIO_CHECK_EQUAL(to_min, lut->to_min);
1032    OIIO_CHECK_EQUAL(to_max, lut->to_max);
1033    OIIO_CHECK_EQUAL(black, lut->hdlblack);
1034    OIIO_CHECK_EQUAL(white, lut->hdlwhite);
1035    
1036    // check 1D data (each channel has the same data)
1037    for(int c = 0; c < 3; ++c) {
1038        OIIO_CHECK_EQUAL(from_min, lut->lut1D->from_min[c]);
1039        OIIO_CHECK_EQUAL(from_max, lut->lut1D->from_max[c]);
1040
1041        OIIO_CHECK_EQUAL(9, lut->lut1D->luts[c].size());
1042
1043        for(unsigned int i = 0; i < lut->lut1D->luts[c].size(); ++i) {
1044            OIIO_CHECK_EQUAL(lut1d[i], lut->lut1D->luts[c][i]);
1045        }
1046    }
1047}
1048
1049OIIO_ADD_TEST(FileFormatHDL, Bake1D)
1050{
1051    
1052    OCIO::ConfigRcPtr config = OCIO::Config::Create();
1053    
1054    // Add lnf space
1055    {
1056        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1057        cs->setName("lnf");
1058        cs->setFamily("lnf");
1059        config->addColorSpace(cs);
1060        config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
1061    }
1062    
1063    // Add target space
1064    {
1065        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1066        cs->setName("target");
1067        cs->setFamily("target");
1068        OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
1069        
1070        float rgb[3] = {0.1f, 0.1f, 0.1f};
1071        transform1->setOffset(rgb);
1072        
1073        cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
1074        config->addColorSpace(cs);
1075    }
1076        
1077    std::string bout =
1078    "Version\t\t1\n"
1079    "Format\t\tany\n"
1080    "Type\t\tRGB\n"
1081    "From\t\t0.000000 1.000000\n"
1082    "To\t\t0.000000 1.000000\n"
1083    "Black\t\t0.000000\n"
1084    "White\t\t1.000000\n"
1085    "Length\t\t10\n"
1086    "LUT:\n"
1087    "R {\n"
1088    "\t0.100000\n"
1089    "\t0.211111\n"
1090    "\t0.322222\n"
1091    "\t0.433333\n"
1092    "\t0.544444\n"
1093    "\t0.655556\n"
1094    "\t0.766667\n"
1095    "\t0.877778\n"
1096    "\t0.988889\n"
1097    "\t1.100000\n"
1098    " }\n"
1099    "G {\n"
1100    "\t0.100000\n"
1101    "\t0.211111\n"
1102    "\t0.322222\n"
1103    "\t0.433333\n"
1104    "\t0.544444\n"
1105    "\t0.655556\n"
1106    "\t0.766667\n"
1107    "\t0.877778\n"
1108    "\t0.988889\n"
1109    "\t1.100000\n"
1110    " }\n"
1111    "B {\n"
1112    "\t0.100000\n"
1113    "\t0.211111\n"
1114    "\t0.322222\n"
1115    "\t0.433333\n"
1116    "\t0.544444\n"
1117    "\t0.655556\n"
1118    "\t0.766667\n"
1119    "\t0.877778\n"
1120    "\t0.988889\n"
1121    "\t1.100000\n"
1122    " }\n";
1123    
1124    //
1125    OCIO::BakerRcPtr baker = OCIO::Baker::Create();
1126    baker->setConfig(config);
1127    baker->setFormat("houdini");
1128    baker->setInputSpace("lnf");
1129    baker->setTargetSpace("target");
1130    baker->setCubeSize(10); // FIXME: Misusing the cube size to set the 1D LUT size
1131    std::ostringstream output;
1132    baker->bake(output);
1133    
1134    //std::cerr << "The LUT: " << std::endl << output.str() << std::endl;
1135    //std::cerr << "Expected:" << std::endl << bout << std::endl;
1136    
1137    //
1138    std::vector<std::string> osvec;
1139    OCIO::pystring::splitlines(output.str(), osvec);
1140    std::vector<std::string> resvec;
1141    OCIO::pystring::splitlines(bout, resvec);
1142    OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
1143    for(unsigned int i = 0; i < std::min(osvec.size(), resvec.size()); ++i)
1144        OIIO_CHECK_EQUAL(OCIO::pystring::strip(osvec[i]), OCIO::pystring::strip(resvec[i]));
1145    
1146}
1147
1148OIIO_ADD_TEST(FileFormatHDL, Read3D)
1149{
1150    std::ostringstream strebuf;
1151    strebuf << "Version         2" << "\n";
1152    strebuf << "Format      any" << "\n";
1153    strebuf << "Type        3D" << "\n";
1154    strebuf << "From        0.2 0.9" << "\n";
1155    strebuf << "To      0.001 0.999" << "\n";
1156    strebuf << "Black       0.002" << "\n";
1157    strebuf << "White       0.98" << "\n";
1158    strebuf << "Length      2" << "\n";
1159    strebuf << "LUT:" << "\n";
1160    strebuf << " {" << "\n";
1161    strebuf << " 0 0 0" << "\n";
1162    strebuf << " 0 0 0" << "\n";
1163    strebuf << " 0 0.390735 2.68116e-28" << "\n";
1164    strebuf << " 0 0.390735 0" << "\n";
1165    strebuf << " 0 0 0" << "\n";
1166    strebuf << " 0 0 0.599397" << "\n";
1167    strebuf << " 0 0.601016 0" << "\n";
1168    strebuf << " 0 0.601016 0.917034" << "\n";
1169    strebuf << " }" << "\n";
1170    
1171    std::istringstream simple3D1D;
1172    simple3D1D.str(strebuf.str());
1173    // Load file
1174    OCIO::LocalFileFormat tester;
1175    OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
1176    OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
1177    
1178    //
1179    //float from_min = 0.2;
1180    //float from_max = 0.9;
1181    float to_min = 0.001f;
1182    float to_max = 0.999f;
1183    float black = 0.002f;
1184    float white = 0.98f;
1185    float cube[2 * 2 * 2 * 3 ] = {
1186        0.f, 0.f, 0.f,
1187        0.f, 0.f, 0.f,
1188        0.f, 0.390735f, 2.68116e-28f,
1189        0.f, 0.390735f, 0.f,
1190        0.f, 0.f, 0.f,
1191        0.f, 0.f, 0.599397f,
1192        0.f, 0.601016f, 0.f,
1193        0.f, 0.601016f, 0.917034f };
1194    
1195    //
1196    OIIO_CHECK_EQUAL(to_min, lut->to_min);
1197    OIIO_CHECK_EQUAL(to_max, lut->to_max);
1198    OIIO_CHECK_EQUAL(black, lut->hdlblack);
1199    OIIO_CHECK_EQUAL(white, lut->hdlwhite);
1200    
1201    // check cube data
1202    OIIO_CHECK_EQUAL(2*2*2*3, lut->lut3D->lut.size());
1203
1204    for(unsigned int i = 0; i < lut->lut3D->lut.size(); ++i) {
1205        OIIO_CHECK_EQUAL(cube[i], lut->lut3D->lut[i]);
1206    }
1207}
1208
1209OIIO_ADD_TEST(FileFormatHDL, Bake3D)
1210{
1211    OCIO::ConfigRcPtr config = OCIO::Config::Create();
1212    
1213    // Set luma coef's to simple values
1214    {
1215        float lumaCoef[3] = {0.333f, 0.333f, 0.333f};
1216        config->setDefaultLumaCoefs(lumaCoef);
1217    }
1218    
1219    // Add lnf space
1220    {
1221        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1222        cs->setName("lnf");
1223        cs->setFamily("lnf");
1224        config->addColorSpace(cs);
1225        config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
1226    }
1227    
1228    // Add target space
1229    {
1230        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1231        cs->setName("target");
1232        cs->setFamily("target");
1233        OCIO::CDLTransformRcPtr transform1 = OCIO::CDLTransform::Create();
1234        
1235        // Set saturation to cause channel crosstalk, making a 3D LUT
1236        transform1->setSat(0.5f);
1237
1238        cs->setTransform(transform1, OCIO::COLORSPACE_DIR_FROM_REFERENCE);
1239        config->addColorSpace(cs);
1240    }
1241        
1242    std::string bout = 
1243    "Version\t\t2\n"
1244    "Format\t\tany\n"
1245    "Type\t\t3D\n"
1246    "From\t\t0.000000 1.000000\n"
1247    "To\t\t0.000000 1.000000\n"
1248    "Black\t\t0.000000\n"
1249    "White\t\t1.000000\n"
1250    "Length\t\t2\n"
1251    "LUT:\n"
1252    " {\n"
1253    "\t0.000000 0.000000 0.000000\n"
1254    "\t0.606300 0.106300 0.106300\n"
1255    "\t0.357600 0.857600 0.357600\n"
1256    "\t0.963900 0.963900 0.463900\n"
1257    "\t0.036100 0.036100 0.536100\n"
1258    "\t0.642400 0.142400 0.642400\n"
1259    "\t0.393700 0.893700 0.893700\n"
1260    "\t1.000000 1.000000 1.000000\n"
1261    " }\n";
1262    
1263    //
1264    OCIO::BakerRcPtr baker = OCIO::Baker::Create();
1265    baker->setConfig(config);
1266    baker->setFormat("houdini");
1267    baker->setInputSpace("lnf");
1268    baker->setTargetSpace("target");
1269    baker->setCubeSize(2);
1270    std::ostringstream output;
1271    baker->bake(output);
1272    
1273    //std::cerr << "The LUT: " << std::endl << output.str() << std::endl;
1274    //std::cerr << "Expected:" << std::endl << bout << std::endl;
1275    
1276    //
1277    std::vector<std::string> osvec;
1278    OCIO::pystring::splitlines(output.str(), osvec);
1279    std::vector<std::string> resvec;
1280    OCIO::pystring::splitlines(bout, resvec);
1281    OIIO_CHECK_EQUAL(osvec.size(), resvec.size());
1282    for(unsigned int i = 0; i < std::min(osvec.size(), resvec.size()); ++i)
1283        OIIO_CHECK_EQUAL(OCIO::pystring::strip(osvec[i]), OCIO::pystring::strip(resvec[i]));
1284}
1285
1286OIIO_ADD_TEST(FileFormatHDL, Read3D1D)
1287{
1288    std::ostringstream strebuf;
1289    strebuf << "Version         3" << "\n";
1290    strebuf << "Format      any" << "\n";
1291    strebuf << "Type        3D+1D" << "\n";
1292    strebuf << "From        0.005478 14.080103" << "\n";
1293    strebuf << "To      0 1" << "\n";
1294    strebuf << "Black       0" << "\n";
1295    strebuf << "White       1" << "\n";
1296    strebuf << "Length      2 10" << "\n";
1297    strebuf << "LUT:" << "\n";
1298    strebuf << "Pre {" << "\n";
1299    strebuf << "    0.994922" << "\n";
1300    strebuf << "    0.995052" << "\n";
1301    strebuf << "    0.995181" << "\n";
1302    strebuf << "    0.995310" << "\n";
1303    strebuf << "    0.995439" << "\n";
1304    strebuf << "    0.995568" << "\n";
1305    strebuf << "    0.995697" << "\n";
1306    strebuf << "    0.995826" << "\n";
1307    strebuf << "    0.995954" << "\n";
1308    strebuf << "    0.996082" << "\n";
1309    strebuf << "}" << "\n";
1310    strebuf << "3D {" << "\n";
1311    strebuf << "    0.093776 0.093776 0.093776" << "\n";
1312    strebuf << "    0.105219 0.093776 0.093776" << "\n";
1313    strebuf << "    0.118058 0.093776 0.093776" << "\n";
1314    strebuf << "    0.132463 0.093776 0.093776" << "\n";
1315    strebuf << "    0.148626 0.093776 0.093776" << "\n";
1316    strebuf << "    0.166761 0.093776 0.093776" << "\n";
1317    strebuf << "    0.187109 0.093776 0.093776" << "\n";
1318    strebuf << "    0.209939 0.093776 0.093776" << "\n";
1319    strebuf << "}" << "\n";
1320    
1321    //
1322    float from_min = 0.005478f;
1323    float from_max = 14.080103f;
1324    float to_min = 0.0f;
1325    float to_max = 1.0f;
1326    float black = 0.0f;
1327    float white = 1.0f;
1328    float prelut[10] = { 0.994922f, 0.995052f, 0.995181f, 0.995310f, 0.995439f,
1329        0.995568f, 0.995697f, 0.995826f, 0.995954f, 0.996082f };
1330    float cube[2 * 2 * 2 * 3 ] = {
1331        0.093776f, 0.093776f, 0.093776f,
1332        0.105219f, 0.093776f, 0.093776f,
1333        0.118058f, 0.093776f, 0.093776f,
1334        0.132463f, 0.093776f, 0.093776f,
1335        0.148626f, 0.093776f, 0.093776f,
1336        0.166761f, 0.093776f, 0.093776f,
1337        0.187109f, 0.093776f, 0.093776f,
1338        0.209939f, 0.093776f, 0.093776f };
1339    
1340    std::istringstream simple3D1D;
1341    simple3D1D.str(strebuf.str());
1342    
1343    // Load file
1344    OCIO::LocalFileFormat tester;
1345    OCIO::CachedFileRcPtr cachedFile = tester.Read(simple3D1D);
1346    OCIO::CachedFileHDLRcPtr lut = OCIO::DynamicPtrCast<OCIO::CachedFileHDL>(cachedFile);
1347    
1348    //
1349    OIIO_CHECK_EQUAL(to_min, lut->to_min);
1350    OIIO_CHECK_EQUAL(to_max, lut->to_max);
1351    OIIO_CHECK_EQUAL(black, lut->hdlblack);
1352    OIIO_CHECK_EQUAL(white, lut->hdlwhite);
1353    
1354    // check prelut data (each channel has the same data)
1355    for(int c = 0; c < 3; ++c) {
1356        OIIO_CHECK_EQUAL(from_min, lut->lut1D->from_min[c]);
1357        OIIO_CHECK_EQUAL(from_max, lut->lut1D->from_max[c]);
1358        OIIO_CHECK_EQUAL(10, lut->lut1D->luts[c].size());
1359
1360        for(unsigned int i = 0; i < lut->lut1D->luts[c].size(); ++i) {
1361            OIIO_CHECK_EQUAL(prelut[i], lut->lut1D->luts[c][i]);
1362        }
1363    }
1364
1365    OIIO_CHECK_EQUAL(2*2*2*3, lut->lut3D->lut.size());
1366    
1367    // check cube data
1368    for(unsigned int i = 0; i < lut->lut3D->lut.size(); ++i) {
1369        OIIO_CHECK_EQUAL(cube[i], lut->lut3D->lut[i]);
1370    }
1371}
1372
1373OIIO_ADD_TEST(FileFormatHDL, Bake3D1D)
1374{
1375    // check baker output
1376    OCIO::ConfigRcPtr config = OCIO::Config::Create();
1377    
1378    // Set luma coef's to simple values
1379    {
1380        float lumaCoef[3] = {0.333f, 0.333f, 0.333f};
1381        config->setDefaultLumaCoefs(lumaCoef);
1382    }
1383
1384    // Add lnf space
1385    {
1386        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1387        cs->setName("lnf");
1388        cs->setFamily("lnf");
1389        config->addColorSpace(cs);
1390        config->setRole(OCIO::ROLE_REFERENCE, cs->getName());
1391    }
1392    
1393    // Add shaper space
1394    {
1395        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1396        cs->setName("shaper");
1397        cs->setFamily("shaper");
1398        OCIO::ExponentTransformRcPtr transform1 = OCIO::ExponentTransform::Create();
1399        float test[4] = {2.6f, 2.6f, 2.6f, 1.0f};
1400        transform1->setValue(test);
1401        cs->setTransform(transform1, OCIO::COLORSPACE_DIR_TO_REFERENCE);
1402        config->addColorSpace(cs);
1403    }
1404    
1405    // Add target space
1406    {
1407        OCIO::ColorSpaceRcPtr cs = OCIO::ColorSpace::Create();
1408        cs->setName("target");
1409        cs->setFam

Large files files are truncated, but you can click here to view the full file