1 /* 2 Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al. 3 All Rights Reserved. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are 7 met: 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. 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <OpenColorIO/OpenColorIO.h> 30 31 #include "FileTransform.h" 32 #include "Lut1DOp.h" 33 #include "pystring/pystring.h" 34 35 #include <cstdio> 36 #include <sstream> 37 38 /* 39 Version 1 40 From -7.5 3.7555555555555555 41 Components 1 42 Length 4096 43 { 44 0.031525943963232252 45 0.045645604561056156 46 ... 47 } 48 49 */ 50 51 52 OCIO_NAMESPACE_ENTER 53 { 54 //////////////////////////////////////////////////////////////// 55 56 namespace 57 { 58 class LocalCachedFile : public CachedFile 59 { 60 public: 61 LocalCachedFile() 62 { 63 lut = Lut1D::Create(); 64 }; 65 ~LocalCachedFile() {}; 66 67 Lut1DRcPtr lut; 68 }; 69 70 typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr; 71 72 73 class LocalFileFormat : public FileFormat 74 { 75 public: 76 77 ~LocalFileFormat() {}; 78 79 virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const; 80 81 virtual CachedFileRcPtr Read(std::istream & istream) const; 82 83 virtual void BuildFileOps(OpRcPtrVec & ops, 84 const Config& config, 85 const ConstContextRcPtr & context, 86 CachedFileRcPtr untypedCachedFile, 87 const FileTransform& fileTransform, 88 TransformDirection dir) const; 89 }; 90 91 void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const 92 { 93 FormatInfo info; 94 info.name = "spi1d"; 95 info.extension = "spi1d"; 96 info.capabilities = FORMAT_CAPABILITY_READ; 97 formatInfoVec.push_back(info); 98 } 99 100 // Try and load the format 101 // Raise an exception if it can't be loaded. 102 103 CachedFileRcPtr LocalFileFormat::Read(std::istream & istream) const 104 { 105 Lut1DRcPtr lut1d = Lut1D::Create(); 106 107 // Parse Header Info 108 int lut_size = -1; 109 float from_min = 0.0; 110 float from_max = 1.0; 111 int version = -1; 112 int components = -1; 113 114 const int MAX_LINE_SIZE = 4096; 115 char lineBuffer[MAX_LINE_SIZE]; 116 117 // PARSE HEADER INFO 118 { 119 std::string headerLine(""); 120 do 121 { 122 istream.getline(lineBuffer, MAX_LINE_SIZE); 123 headerLine = std::string(lineBuffer); 124 if(pystring::startswith(headerLine, "Version")) 125 { 126 if(sscanf(lineBuffer, "Version %d", &version)!=1) 127 throw Exception("Invalid 'Version' Tag"); 128 } 129 else if(pystring::startswith(headerLine, "From")) 130 { 131 if(sscanf(lineBuffer, "From %f %f", &from_min, &from_max)!=2) 132 throw Exception("Invalid 'From' Tag"); 133 } 134 else if(pystring::startswith(headerLine, "Components")) 135 { 136 if(sscanf(lineBuffer, "Components %d", &components)!=1) 137 throw Exception("Invalid 'Components' Tag"); 138 } 139 else if(pystring::startswith(headerLine, "Length")) 140 { 141 if(sscanf(lineBuffer, "Length %d", &lut_size)!=1) 142 throw Exception("Invalid 'Length' Tag"); 143 } 144 } 145 while (istream.good() && !pystring::startswith(headerLine,"{")); 146 } 147 148 if(version == -1) 149 throw Exception("Could not find 'Version' Tag"); 150 if(version != 1) 151 throw Exception("Only format version 1 supported."); 152 if (lut_size == -1) 153 throw Exception("Could not find 'Length' Tag"); 154 if (components == -1) 155 throw Exception("Could not find 'Components' Tag"); 156 if (components<0 || components>3) 157 throw Exception("Components must be [1,2,3]"); 158 159 160 161 for(int i=0; i<3; ++i) 162 { 163 lut1d->from_min[i] = from_min; 164 lut1d->from_max[i] = from_max; 165 lut1d->luts[i].clear(); 166 lut1d->luts[i].reserve(lut_size); 167 } 168 169 { 170 istream.getline(lineBuffer, MAX_LINE_SIZE); 171 172 int lineCount=0; 173 float values[4]; 174 175 while (istream.good()) 176 { 177 // If 1 component is specificed, use x1 x1 x1 defaultA 178 if(components==1 && sscanf(lineBuffer,"%f",&values[0])==1) 179 { 180 lut1d->luts[0].push_back(values[0]); 181 lut1d->luts[1].push_back(values[0]); 182 lut1d->luts[2].push_back(values[0]); 183 ++lineCount; 184 } 185 // If 2 components are specificed, use x1 x2 0.0 186 else if(components==2 && sscanf(lineBuffer,"%f %f",&values[0],&values[1])==2) 187 { 188 lut1d->luts[0].push_back(values[0]); 189 lut1d->luts[1].push_back(values[1]); 190 lut1d->luts[2].push_back(0.0); 191 ++lineCount; 192 } 193 // If 3 component is specificed, use x1 x2 x3 defaultA 194 else if(components==3 && sscanf(lineBuffer,"%f %f %f",&values[0],&values[1],&values[2])==3) 195 { 196 lut1d->luts[0].push_back(values[0]); 197 lut1d->luts[1].push_back(values[1]); 198 lut1d->luts[2].push_back(values[2]); 199 ++lineCount; 200 } 201 202 if(lineCount == lut_size) break; 203 204 istream.getline(lineBuffer, MAX_LINE_SIZE); 205 } 206 207 if(lineCount!=lut_size) 208 throw Exception("Not enough entries found."); 209 } 210 211 // 1e-5 rel error is a good threshold when float numbers near 0 212 // are written out with 6 decimal places of precision. This is 213 // a bit aggressive, I.e., changes in the 6th decimal place will 214 // be considered roundoff error, but changes in the 5th decimal 215 // will be considered lut 'intent'. 216 // 1.0 217 // 1.000005 equal to 1.0 218 // 1.000007 equal to 1.0 219 // 1.000010 not equal 220 // 0.0 221 // 0.000001 not equal 222 lut1d->maxerror = 1e-5f; 223 lut1d->errortype = ERROR_RELATIVE; 224 225 LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile()); 226 cachedFile->lut = lut1d; 227 return cachedFile; 228 } 229 230 void LocalFileFormat::BuildFileOps(OpRcPtrVec & ops, 231 const Config& /*config*/, 232 const ConstContextRcPtr & /*context*/, 233 CachedFileRcPtr untypedCachedFile, 234 const FileTransform& fileTransform, 235 TransformDirection dir) const 236 { 237 LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile); 238 239 if(!cachedFile) // This should never happen. 240 { 241 std::ostringstream os; 242 os << "Cannot build Spi1D Op. Invalid cache type."; 243 throw Exception(os.str().c_str()); 244 } 245 246 TransformDirection newDir = CombineTransformDirections(dir, 247 fileTransform.getDirection()); 248 249 CreateLut1DOp(ops, 250 cachedFile->lut, 251 fileTransform.getInterpolation(), 252 newDir); 253 } 254 } 255 256 FileFormat * CreateFileFormatSpi1D() 257 { 258 return new LocalFileFormat(); 259 } 260 } 261 OCIO_NAMESPACE_EXIT 262