void LoadCDL(CDLTransform * cdl, TiXmlElement * root) { if(!cdl) return; if(!root) { std::ostringstream os; os << "Error loading CDL xml. "; os << "Null root element."; throw Exception(os.str().c_str()); } if(std::string(root->Value()) != "ColorCorrection") { std::ostringstream os; os << "Error loading CDL xml. "; os << "Root element is type '" << root->Value() << "', "; os << "ColorCorrection expected."; throw Exception(os.str().c_str()); } TiXmlHandle handle( root ); const char * id = root->Attribute("id"); if(!id) id = ""; cdl->setID(id); TiXmlElement* desc = handle.FirstChild( "SOPNode" ).FirstChild("Description").ToElement(); if(desc) { const char * text = desc->GetText(); if(text) cdl->setDescription(text); } std::vector<std::string> lineParts; std::vector<float> floatArray; TiXmlElement* slope = handle.FirstChild( "SOPNode" ).FirstChild("Slope").ToElement(); if(slope) { const char * text = slope->GetText(); if(text) { pystring::split(pystring::strip(text), lineParts); if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts))) { std::ostringstream os; os << "Error loading CDL xml. "; os << id << ".SOPNode.Slope text '"; os << text << "' is not convertible to 3 floats."; throw Exception(os.str().c_str()); } cdl->setSlope(&floatArray[0]); } } TiXmlElement* offset = handle.FirstChild( "SOPNode" ).FirstChild("Offset").ToElement(); if(offset) { const char * text = offset->GetText(); if(text) { pystring::split(pystring::strip(text), lineParts); if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts))) { std::ostringstream os; os << "Error loading CDL xml. "; os << id << ".SOPNode.Offset text '"; os << text << "' is not convertible to 3 floats."; throw Exception(os.str().c_str()); } cdl->setOffset(&floatArray[0]); } } TiXmlElement* power = handle.FirstChild( "SOPNode" ).FirstChild("Power").ToElement(); if(power) { const char * text = power->GetText(); if(text) { pystring::split(pystring::strip(text), lineParts); if((lineParts.size() != 3) || (!StringVecToFloatVec(floatArray, lineParts))) { std::ostringstream os; os << "Error loading CDL xml. "; os << id << ".SOPNode.Power text '"; os << text << "' is not convertible to 3 floats."; throw Exception(os.str().c_str()); } cdl->setPower(&floatArray[0]); } } TiXmlElement* sat = handle.FirstChild( "SatNode" ).FirstChild("Saturation").ToElement(); if(sat) { const char * text = sat->GetText(); if(text) { float satval = 1.0f; if(!StringToFloat(&satval, text)) { std::ostringstream os; os << "Error loading CDL xml. "; os << id << ".SatNode.Saturation text '"; os << text << "' is not convertible to float."; throw Exception(os.str().c_str()); } cdl->setSat(satval); } } }
CachedFileRcPtr LocalFileFormat::Read( std::istream & istream, const std::string & fileName) const { // this shouldn't happen if(!istream) { throw Exception ("File stream empty when trying to read Iridas .cube lut"); } // Parse the file std::vector<float> raw; int size3d[] = { 0, 0, 0 }; int size1d = 0; bool in1d = false; bool in3d = false; float domain_min[] = { 0.0f, 0.0f, 0.0f }; float domain_max[] = { 1.0f, 1.0f, 1.0f }; { std::string line; std::vector<std::string> parts; std::vector<float> tmpfloats; int lineNumber = 0; while(nextline(istream, line)) { ++lineNumber; // All lines starting with '#' are comments if(pystring::startswith(line,"#")) continue; // Strip, lowercase, and split the line pystring::split(pystring::lower(pystring::strip(line)), parts); if(parts.empty()) continue; if(pystring::lower(parts[0]) == "title") { // Optional, and currently unhandled } else if(pystring::lower(parts[0]) == "lut_1d_size") { if(parts.size() != 2 || !StringToInt( &size1d, parts[1].c_str())) { ThrowErrorMessage( "Malformed LUT_1D_SIZE tag.", fileName, lineNumber, line); } raw.reserve(3*size1d); in1d = true; } else if(pystring::lower(parts[0]) == "lut_2d_size") { ThrowErrorMessage( "Unsupported tag: 'LUT_2D_SIZE'.", fileName, lineNumber, line); } else if(pystring::lower(parts[0]) == "lut_3d_size") { int size = 0; if(parts.size() != 2 || !StringToInt( &size, parts[1].c_str())) { ThrowErrorMessage( "Malformed LUT_3D_SIZE tag.", fileName, lineNumber, line); } size3d[0] = size; size3d[1] = size; size3d[2] = size; raw.reserve(3*size3d[0]*size3d[1]*size3d[2]); in3d = true; } else if(pystring::lower(parts[0]) == "domain_min") { if(parts.size() != 4 || !StringToFloat( &domain_min[0], parts[1].c_str()) || !StringToFloat( &domain_min[1], parts[2].c_str()) || !StringToFloat( &domain_min[2], parts[3].c_str())) { ThrowErrorMessage( "Malformed DOMAIN_MIN tag.", fileName, lineNumber, line); } } else if(pystring::lower(parts[0]) == "domain_max") { if(parts.size() != 4 || !StringToFloat( &domain_max[0], parts[1].c_str()) || !StringToFloat( &domain_max[1], parts[2].c_str()) || !StringToFloat( &domain_max[2], parts[3].c_str())) { ThrowErrorMessage( "Malformed DOMAIN_MAX tag.", fileName, lineNumber, line); } } else { // It must be a float triple! if(!StringVecToFloatVec(tmpfloats, parts) || tmpfloats.size() != 3) { ThrowErrorMessage( "Malformed color triples specified.", fileName, lineNumber, line); } for(int i=0; i<3; ++i) { raw.push_back(tmpfloats[i]); } } } } // Interpret the parsed data, validate lut sizes LocalCachedFileRcPtr cachedFile = LocalCachedFileRcPtr(new LocalCachedFile()); if(in1d) { if(size1d != static_cast<int>(raw.size()/3)) { std::ostringstream os; os << "Incorrect number of lut1d entries. "; os << "Found " << raw.size() / 3; os << ", expected " << size1d << "."; ThrowErrorMessage( os.str().c_str(), fileName, -1, ""); } // Reformat 1D data if(size1d>0) { cachedFile->has1D = true; memcpy(cachedFile->lut1D->from_min, domain_min, 3*sizeof(float)); memcpy(cachedFile->lut1D->from_max, domain_max, 3*sizeof(float)); for(int channel=0; channel<3; ++channel) { cachedFile->lut1D->luts[channel].resize(size1d); for(int i=0; i<size1d; ++i) { cachedFile->lut1D->luts[channel][i] = raw[3*i+channel]; } } // 1e-5 rel error is a good threshold when float numbers near 0 // are written out with 6 decimal places of precision. This is // a bit aggressive, I.e., changes in the 6th decimal place will // be considered roundoff error, but changes in the 5th decimal // will be considered lut 'intent'. // 1.0 // 1.000005 equal to 1.0 // 1.000007 equal to 1.0 // 1.000010 not equal // 0.0 // 0.000001 not equal cachedFile->lut1D->maxerror = 1e-5f; cachedFile->lut1D->errortype = ERROR_RELATIVE; } } else if(in3d) { cachedFile->has3D = true; if(size3d[0]*size3d[1]*size3d[2] != static_cast<int>(raw.size()/3)) { std::ostringstream os; os << "Incorrect number of lut3d entries. "; os << "Found " << raw.size() / 3 << ", expected "; os << size3d[0] * size3d[1] * size3d[2] << "."; ThrowErrorMessage( os.str().c_str(), fileName, -1, ""); } // Reformat 3D data memcpy(cachedFile->lut3D->from_min, domain_min, 3*sizeof(float)); memcpy(cachedFile->lut3D->from_max, domain_max, 3*sizeof(float)); cachedFile->lut3D->size[0] = size3d[0]; cachedFile->lut3D->size[1] = size3d[1]; cachedFile->lut3D->size[2] = size3d[2]; cachedFile->lut3D->lut = raw; } else { ThrowErrorMessage( "Lut type (1D/3D) unspecified.", fileName, -1, ""); } return cachedFile; }