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 <iterator> 32 #include <algorithm> 33 34 #include <OpenColorIO/OpenColorIO.h> 35 36 #include "FileTransform.h" 37 #include "Lut1DOp.h" 38 #include "Lut3DOp.h" 39 #include "ParseUtils.h" 40 #include "pystring/pystring.h" 41 42 /* 43 44 Iridas itx format 45 LUT_3D_SIZE M 46 47 #LUT_3D_SIZE M 48 #where M is the size of the texture 49 #a 3D texture has the size M x M x M 50 #e.g. LUT_3D_SIZE 16 creates a 16 x 16 x 16 3D texture 51 52 #for 1D textures, the data is simply a list of floating point values, 53 #three per line, in RGB order 54 #for 3D textures, the data is also RGB, and ordered in such a way 55 #that the red coordinate changes fastest, then the green coordinate, 56 #and finally, the blue coordinate changes slowest: 57 0.0 0.0 0.0 58 1.0 0.0 0.0 59 0.0 1.0 0.0 60 1.0 1.0 0.0 61 0.0 0.0 1.0 62 1.0 0.0 1.0 63 0.0 1.0 1.0 64 1.0 1.0 1.0 65 */ 66 67 68 OCIO_NAMESPACE_ENTER 69 { 70 namespace 71 { 72 class LocalCachedFile : public CachedFile 73 { 74 public: 75 LocalCachedFile () 76 { 77 lut3D = Lut3D::Create(); 78 }; 79 ~LocalCachedFile() {}; 80 81 Lut3DRcPtr lut3D; 82 }; 83 84 typedef OCIO_SHARED_PTR<LocalCachedFile> LocalCachedFileRcPtr; 85 86 87 88 class LocalFileFormat : public FileFormat 89 { 90 public: 91 92 ~LocalFileFormat() {}; 93 94 virtual void GetFormatInfo(FormatInfoVec & formatInfoVec) const; 95 96 virtual CachedFileRcPtr Read(std::istream & istream) const; 97 98 virtual void Write(const Baker & baker, 99 const std::string & formatName, 100 std::ostream & ostream) const; 101 102 virtual void BuildFileOps(OpRcPtrVec & ops, 103 const Config& config, 104 const ConstContextRcPtr & context, 105 CachedFileRcPtr untypedCachedFile, 106 const FileTransform& fileTransform, 107 TransformDirection dir) const; 108 }; 109 110 void LocalFileFormat::GetFormatInfo(FormatInfoVec & formatInfoVec) const 111 { 112 FormatInfo info; 113 info.name = "iridas_itx"; 114 info.extension = "itx"; 115 info.capabilities = (FORMAT_CAPABILITY_READ | FORMAT_CAPABILITY_WRITE); 116 formatInfoVec.push_back(info); 117 } 118 119 CachedFileRcPtr 120 LocalFileFormat::Read(std::istream & istream) const 121 { 122 // this shouldn't happen 123 if(!istream) 124 { 125 throw Exception ("File stream empty when trying to read Iridas .itx lut"); 126 } 127 128 // Parse the file 129 std::vector<float> raw; 130 131 int size3d[] = { 0, 0, 0 }; 132 bool in3d = false; 133 134 { 135 std::string line; 136 std::vector<std::string> parts; 137 std::vector<float> tmpfloats; 138 139 while(nextline(istream, line)) 140 { 141 // All lines starting with '#' are comments 142 if(pystring::startswith(line,"#")) continue; 143 144 // Strip, lowercase, and split the line 145 pystring::split(pystring::lower(pystring::strip(line)), parts); 146 if(parts.empty()) continue; 147 148 if(pystring::lower(parts[0]) == "lut_3d_size") 149 { 150 int size = 0; 151 152 if(parts.size() != 2 || !StringToInt( &size, parts[1].c_str())) 153 { 154 throw Exception("Malformed LUT_3D_SIZE tag in Iridas .itx lut."); 155 } 156 size3d[0] = size; 157 size3d[1] = size; 158 size3d[2] = size; 159 160 raw.reserve(3*size3d[0]*size3d[1]*size3d[2]); 161 in3d = true; 162 } 163 else if(in3d) 164 { 165 // It must be a float triple! 166 167 if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3) 168 { 169 std::ostringstream os; 170 os << "Malformed color triples specified in Iridas .itx lut:"; 171 os << "'" << line << "'."; 172 throw Exception(os.str().c_str()); 173 } 174 175 for(int i=0; i<3; ++i) 176 { 177 raw.push_back(tmpfloats[i]); 178 } 179 } 180 } 181 } 182 183 // Interpret the parsed data, validate lut sizes 184 185 LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile()); 186 187 if(in3d) 188 { 189 if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3)) 190 { 191 std::ostringstream os; 192 os << "Parse error in Iridas .itx lut. "; 193 os << "Incorrect number of lut3d entries. "; 194 os << "Found " << raw.size()/3 << ", expected " << size3d[0]*size3d[1]*size3d[2] << "."; 195 throw Exception(os.str().c_str()); 196 } 197 198 // Reformat 3D data 199 cachedFile->lut3D->size[0] = size3d[0]; 200 cachedFile->lut3D->size[1] = size3d[1]; 201 cachedFile->lut3D->size[2] = size3d[2]; 202 cachedFile->lut3D->lut = raw; 203 } 204 else 205 { 206 std::ostringstream os; 207 os << "Parse error in Iridas .itx lut. "; 208 os << "Lut type (1D/3D) unspecified."; 209 throw Exception(os.str().c_str()); 210 } 211 212 return cachedFile; 213 } 214 215 void LocalFileFormat::Write(const Baker & baker, 216 const std::string & formatName, 217 std::ostream & ostream) const 218 { 219 int DEFAULT_CUBE_SIZE = 64; 220 221 if(formatName != "iridas_itx") 222 { 223 std::ostringstream os; 224 os << "Unknown 3dl format name, '"; 225 os << formatName << "'."; 226 throw Exception(os.str().c_str()); 227 } 228 229 ConstConfigRcPtr config = baker.getConfig(); 230 231 int cubeSize = baker.getCubeSize(); 232 if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE; 233 cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2 234 235 std::vector<float> cubeData; 236 cubeData.resize(cubeSize*cubeSize*cubeSize*3); 237 GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED); 238 PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3); 239 240 // Apply our conversion from the input space to the output space. 241 ConstProcessorRcPtr inputToTarget; 242 std::string looks = baker.getLooks(); 243 if (!looks.empty()) 244 { 245 LookTransformRcPtr transform = LookTransform::Create(); 246 transform->setLooks(looks.c_str()); 247 transform->setSrc(baker.getInputSpace()); 248 transform->setDst(baker.getTargetSpace()); 249 inputToTarget = config->getProcessor(transform, 250 TRANSFORM_DIR_FORWARD); 251 } 252 else 253 { 254 inputToTarget = config->getProcessor(baker.getInputSpace(), 255 baker.getTargetSpace()); 256 } 257 inputToTarget->apply(cubeImg); 258 259 // Write out the file. 260 // For for maximum compatibility with other apps, we will 261 // not utilize the shaper or output any metadata 262 263 ostream << "LUT_3D_SIZE " << cubeSize << "\n"; 264 if(cubeSize < 2) 265 { 266 throw Exception("Internal cube size exception."); 267 } 268 269 // Set to a fixed 6 decimal precision 270 ostream.setf(std::ios::fixed, std::ios::floatfield); 271 ostream.precision(6); 272 for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i) 273 { 274 float r = cubeData[3*i+0]; 275 float g = cubeData[3*i+1]; 276 float b = cubeData[3*i+2]; 277 ostream << r << " " << g << " " << b << "\n"; 278 } 279 ostream << "\n"; 280 } 281 282 283 void 284 LocalFileFormat::BuildFileOps(OpRcPtrVec & ops, 285 const Config& /*config*/, 286 const ConstContextRcPtr & /*context*/, 287 CachedFileRcPtr untypedCachedFile, 288 const FileTransform& fileTransform, 289 TransformDirection dir) const 290 { 291 LocalCachedFileRcPtr cachedFile = DynamicPtrCast<LocalCachedFile>(untypedCachedFile); 292 293 // This should never happen. 294 if(!cachedFile) 295 { 296 std::ostringstream os; 297 os << "Cannot build Iridas .itx Op. Invalid cache type."; 298 throw Exception(os.str().c_str()); 299 } 300 301 TransformDirection newDir = CombineTransformDirections(dir, 302 fileTransform.getDirection()); 303 if(newDir == TRANSFORM_DIR_UNKNOWN) 304 { 305 std::ostringstream os; 306 os << "Cannot build file format transform,"; 307 os << " unspecified transform direction."; 308 throw Exception(os.str().c_str()); 309 } 310 311 // TODO: INTERP_LINEAR should not be hard-coded. 312 // Instead query 'highest' interpolation? 313 // (right now, it's linear). If cubic is added, consider 314 // using it 315 316 if(newDir == TRANSFORM_DIR_FORWARD) 317 { 318 CreateLut3DOp(ops, cachedFile->lut3D, 319 fileTransform.getInterpolation(), newDir); 320 } 321 else if(newDir == TRANSFORM_DIR_INVERSE) 322 { 323 CreateLut3DOp(ops, cachedFile->lut3D, 324 fileTransform.getInterpolation(), newDir); 325 } 326 } 327 } 328 329 FileFormat * CreateFileFormatIridasItx() 330 { 331 return new LocalFileFormat(); 332 } 333 } 334 OCIO_NAMESPACE_EXIT 335 336 337 /////////////////////////////////////////////////////////////////////////////// 338