void Processor::Impl::getGpuLut3D(float* lut3d, const GpuShaderDesc & shaderDesc) const { if(!lut3d) return; AutoMutex lock(m_resultsCacheMutex); if(m_lastShaderDesc != shaderDesc.getCacheID()) { m_lastShaderDesc = shaderDesc.getCacheID(); m_shader = ""; m_shaderCacheID = ""; m_lut3D.clear(); m_lut3DCacheID = ""; } int lut3DEdgeLen = shaderDesc.getLut3DEdgeLen(); int lut3DNumPixels = lut3DEdgeLen*lut3DEdgeLen*lut3DEdgeLen; // Can we write the entire shader using only shader text? // If so, the lut3D is not needed so clear it. // This is preferable to identity, as it lets people notice if // it's accidentally being used. if(m_gpuOpsCpuLatticeProcess.empty()) { memset(lut3d, 0, sizeof(float) * 3 * lut3DNumPixels); return; } if(m_lut3D.empty()) { // Allocate 3dlut image, RGBA m_lut3D.resize(lut3DNumPixels*4); GenerateIdentityLut3D(&m_lut3D[0], lut3DEdgeLen, 4); // Apply the lattice ops to it for(int i=0; i<(int)m_gpuOpsCpuLatticeProcess.size(); ++i) { m_gpuOpsCpuLatticeProcess[i]->apply(&m_lut3D[0], lut3DNumPixels); } // Convert the RGBA image to an RGB image, in place. // Of course, this only works because we're doing it from left to right // so old pixels are read before they're written over // TODO: is this bad for memory access patterns? // see if this is faster with a 2nd temp float array for(int i=1; i<lut3DNumPixels; ++i) // skip the 1st pixel, it's ok. { m_lut3D[3*i+0] = m_lut3D[4*i+0]; m_lut3D[3*i+1] = m_lut3D[4*i+1]; m_lut3D[3*i+2] = m_lut3D[4*i+2]; } } // Copy to the destination memcpy(lut3d, &m_lut3D[0], sizeof(float) * 3 * lut3DNumPixels); }
void Generate(int cubesize, int maxwidth, const std::string & outputfile, const std::string & configfile, const std::string & incolorspace, const std::string & outcolorspace) { int width = 0; int height = 0; int numchannels = 3; GetLutImageSize(width, height, cubesize, maxwidth); std::vector<float> img; img.resize(width*height*numchannels, 0); GenerateIdentityLut3D(&img[0], cubesize, numchannels, LUT3DORDER_FAST_RED); if(!incolorspace.empty() || !outcolorspace.empty()) { OCIO::ConstConfigRcPtr config = OCIO::Config::Create(); if(!configfile.empty()) { config = OCIO::Config::CreateFromFile(configfile.c_str()); } else if(getenv("OCIO")) { config = OCIO::Config::CreateFromEnv(); } else { std::ostringstream os; os << "You must specify an ocio configuration "; os << "(either with --config or $OCIO)."; throw Exception(os.str().c_str()); } OCIO::ConstProcessorRcPtr processor = config->getProcessor(incolorspace.c_str(), outcolorspace.c_str()); OCIO::PackedImageDesc imgdesc(&img[0], width, height, 3); processor->apply(imgdesc); } OIIO::ImageOutput* f = OIIO::ImageOutput::create(outputfile); if(!f) { throw Exception( "Could not create output image."); } OIIO::ImageSpec spec(width, height, numchannels, OIIO::TypeDesc::TypeFloat); // TODO: If DPX, force 16-bit output? f->open(outputfile, spec); f->write_image(OIIO::TypeDesc::FLOAT, &img[0]); f->close(); delete f; }
void LocalFileFormat::Write(const Baker & baker, const std::string & formatName, std::ostream & ostream) const { static const int DEFAULT_CUBE_SIZE = 32; if(formatName != "iridas_cube") { std::ostringstream os; os << "Unknown cube format name, '"; os << formatName << "'."; throw Exception(os.str().c_str()); } ConstConfigRcPtr config = baker.getConfig(); int cubeSize = baker.getCubeSize(); if(cubeSize==-1) cubeSize = DEFAULT_CUBE_SIZE; cubeSize = std::max(2, cubeSize); // smallest cube is 2x2x2 std::vector<float> cubeData; cubeData.resize(cubeSize*cubeSize*cubeSize*3); GenerateIdentityLut3D(&cubeData[0], cubeSize, 3, LUT3DORDER_FAST_RED); PackedImageDesc cubeImg(&cubeData[0], cubeSize*cubeSize*cubeSize, 1, 3); // Apply our conversion from the input space to the output space. ConstProcessorRcPtr inputToTarget; std::string looks = baker.getLooks(); if(!looks.empty()) { LookTransformRcPtr transform = LookTransform::Create(); transform->setLooks(looks.c_str()); transform->setSrc(baker.getInputSpace()); transform->setDst(baker.getTargetSpace()); inputToTarget = config->getProcessor(transform, TRANSFORM_DIR_FORWARD); } else { inputToTarget = config->getProcessor(baker.getInputSpace(), baker.getTargetSpace()); } inputToTarget->apply(cubeImg); if(baker.getMetadata() != NULL) { std::string metadata = baker.getMetadata(); std::vector<std::string> metadatavec; pystring::split(pystring::strip(metadata), metadatavec, "\n"); if(metadatavec.size() > 0) { for(size_t i = 0; i < metadatavec.size(); ++i) { ostream << "# " << metadatavec[i] << "\n"; } ostream << "\n"; } } ostream << "LUT_3D_SIZE " << cubeSize << "\n"; if(cubeSize < 2) { throw Exception("Internal cube size exception"); } // Set to a fixed 6 decimal precision ostream.setf(std::ios::fixed, std::ios::floatfield); ostream.precision(6); for(int i=0; i<cubeSize*cubeSize*cubeSize; ++i) { ostream << cubeData[3*i+0] << " " << cubeData[3*i+1] << " " << cubeData[3*i+2] << "\n"; } }