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 <cstdio> 30 #include <cstring> 31 #include <iostream> 32 #include <iterator> 33 34 #include <OpenColorIO/OpenColorIO.h> 35 36 #include "FileTransform.h" 37 #include "Lut3DOp.h" 38 #include "MatrixOps.h" 39 #include "ParseUtils.h" 40 #include "pystring/pystring.h" 41 42 OCIO_NAMESPACE_ENTER 43 { 44 namespace 45 { 46 class LocalCachedFile : public CachedFile 47 { 48 public: 49 LocalCachedFile () : 50 useMatrix(false) 51 { 52 lut3D = Lut3D::Create(); 53 memset(m44, 0, 16*sizeof(float)); 54 }; 55 ~LocalCachedFile() {}; 56 57 Lut3DRcPtr lut3D; 58 float m44[16]; 59 bool useMatrix; 60 }; 61 62 typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr; 63 64 65 66 class LocalFileFormat : public FileFormat 67 { 68 public: 69 70 ~LocalFileFormat() {}; 71 72 virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const; 73 74 virtual CachedFileRcPtr Read(std::istream & istream) const; 75 76 virtual void BuildFileOps(OpRcPtrVec & ops, 77 const Config& config, 78 const ConstContextRcPtr & context, 79 CachedFileRcPtr untypedCachedFile, 80 const FileTransform& fileTransform, 81 TransformDirection dir) const; 82 }; 83 84 void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const 85 { 86 FormatInfo info; 87 info.name = "nukevf"; 88 info.extension = "vf"; 89 info.capabilities = FORMAT_CAPABILITY_READ; 90 formatInfoVec.push_back(info); 91 } 92 93 CachedFileRcPtr 94 LocalFileFormat::Read(std::istream & istream) const 95 { 96 // this shouldn't happen 97 if(!istream) 98 { 99 throw Exception ("File stream empty when trying to read .vf lut"); 100 } 101 102 // Validate the file type 103 std::string line; 104 if(!nextline(istream, line) || 105 !pystring::startswith(pystring::lower(line), "#inventor")) 106 { 107 throw Exception("Lut doesn't seem to be a .vf lut. Expecting '#Inventor V2.1 ascii'."); 108 } 109 110 // Parse the file 111 std::vector<float> raw3d; 112 int size3d[] = { 0, 0, 0 }; 113 std::vector<float> global_transform; 114 115 { 116 std::vector<std::string> parts; 117 std::vector<float> tmpfloats; 118 119 bool in3d = false; 120 121 while(nextline(istream, line)) 122 { 123 // Strip, lowercase, and split the line 124 pystring::split(pystring::lower(pystring::strip(line)), parts); 125 126 if(parts.empty()) continue; 127 128 if(pystring::startswith(parts[0],"#")) continue; 129 130 if(!in3d) 131 { 132 if(parts[0] == "grid_size") 133 { 134 if(parts.size() != 4 || 135 !StringToInt( &size3d[0], parts[1].c_str()) || 136 !StringToInt( &size3d[1], parts[2].c_str()) || 137 !StringToInt( &size3d[2], parts[3].c_str())) 138 { 139 throw Exception("Malformed grid_size tag in .vf lut."); 140 } 141 142 raw3d.reserve(3*size3d[0]*size3d[1]*size3d[2]); 143 } 144 else if(parts[0] == "global_transform") 145 { 146 if(parts.size() != 17) 147 { 148 throw Exception("Malformed global_transform tag. 16 floats expected."); 149 } 150 151 parts.erase(parts.begin()); // Drop the 1st entry. (the tag) 152 if(!StringVecToFloatVec(global_transform, parts) || global_transform.size() != 16) 153 { 154 throw Exception("Malformed global_transform tag. Could not convert to float array."); 155 } 156 } 157 // TODO: element_size (aka scale3) 158 // TODO: world_origin (aka translate3) 159 else if(parts[0] == "data") 160 { 161 in3d = true; 162 } 163 } 164 else // (in3d) 165 { 166 if(StringVecToFloatVec(tmpfloats, parts) && (tmpfloats.size() == 3)) 167 { 168 raw3d.push_back(tmpfloats[0]); 169 raw3d.push_back(tmpfloats[1]); 170 raw3d.push_back(tmpfloats[2]); 171 } 172 } 173 } 174 } 175 176 // Interpret the parsed data, validate lut sizes 177 if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw3d.size()/3)) 178 { 179 std::ostringstream os; 180 os << "Parse error in .vf lut. "; 181 os << "Incorrect number of lut3d entries. "; 182 os << "Found " << raw3d.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << "."; 183 throw Exception(os.str().c_str()); 184 } 185 186 if(size3d[0]*size3d[1]*size3d[2] == 0) 187 { 188 std::ostringstream os; 189 os << "Parse error in .vf lut. "; 190 os << "No 3D Lut entries found."; 191 throw Exception(os.str().c_str()); 192 } 193 194 LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile()); 195 196 // Setup the global matrix. 197 // (Nuke pre-scales this by the 3dlut size, so we must undo that here) 198 if(global_transform.size() == 16) 199 { 200 for(int i=0; i<4; ++i) 201 { 202 global_transform[4*i+0] *= static_cast<float>(size3d[0]); 203 global_transform[4*i+1] *= static_cast<float>(size3d[1]); 204 global_transform[4*i+2] *= static_cast<float>(size3d[2]); 205 } 206 207 memcpy(cachedFile->m44, &global_transform[0], 16*sizeof(float)); 208 cachedFile->useMatrix = true; 209 } 210 211 // Reformat 3D data 212 cachedFile->lut3D->size[0] = size3d[0]; 213 cachedFile->lut3D->size[1] = size3d[1]; 214 cachedFile->lut3D->size[2] = size3d[2]; 215 cachedFile->lut3D->lut.reserve(raw3d.size()); 216 217 for(int rIndex=0; rIndex<size3d[0]; ++rIndex) 218 { 219 for(int gIndex=0; gIndex<size3d[1]; ++gIndex) 220 { 221 for(int bIndex=0; bIndex<size3d[2]; ++bIndex) 222 { 223 int i = GetLut3DIndex_B(rIndex, gIndex, bIndex, 224 size3d[0], size3d[1], size3d[2]); 225 226 cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+0])); 227 cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+1])); 228 cachedFile->lut3D->lut.push_back(static_cast<float>(raw3d[i+2])); 229 } 230 } 231 } 232 233 return cachedFile; 234 } 235 236 void 237 LocalFileFormat::BuildFileOps(OpRcPtrVec & ops, 238 const Config& /*config*/, 239 const ConstContextRcPtr & /*context*/, 240 CachedFileRcPtr untypedCachedFile, 241 const FileTransform& fileTransform, 242 TransformDirection dir) const 243 { 244 LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile); 245 246 // This should never happen. 247 if(!cachedFile) 248 { 249 std::ostringstream os; 250 os << "Cannot build .vf Op. Invalid cache type."; 251 throw Exception(os.str().c_str()); 252 } 253 254 TransformDirection newDir = CombineTransformDirections(dir, 255 fileTransform.getDirection()); 256 if(newDir == TRANSFORM_DIR_UNKNOWN) 257 { 258 std::ostringstream os; 259 os << "Cannot build file format transform,"; 260 os << " unspecified transform direction."; 261 throw Exception(os.str().c_str()); 262 } 263 264 if(newDir == TRANSFORM_DIR_FORWARD) 265 { 266 if(cachedFile->useMatrix) 267 { 268 CreateMatrixOp(ops, cachedFile->m44, newDir); 269 } 270 271 CreateLut3DOp(ops, cachedFile->lut3D, 272 fileTransform.getInterpolation(), newDir); 273 } 274 else if(newDir == TRANSFORM_DIR_INVERSE) 275 { 276 CreateLut3DOp(ops, cachedFile->lut3D, 277 fileTransform.getInterpolation(), newDir); 278 279 if(cachedFile->useMatrix) 280 { 281 CreateMatrixOp(ops, cachedFile->m44, newDir); 282 } 283 } 284 } 285 } 286 287 FileFormat * CreateFileFormatVF() 288 { 289 return new LocalFileFormat(); 290 } 291 } 292 OCIO_NAMESPACE_EXIT 293 294 295 /////////////////////////////////////////////////////////////////////////////// 296 297 // TODO: Unit test 298