void ColorSpaceTransformOp::modifyTypedPrimitive( ImagePrimitive * image, const CompoundObject * operands ) { const InputColorSpace &inputColorSpace = m_inputColorSpaceParameter->getTypedValue(); const OutputColorSpace &outputColorSpace = m_outputColorSpaceParameter->getTypedValue(); if ( inputColorSpace == outputColorSpace ) { return; } std::vector< ConversionInfo > conversions; findConversion( inputColorSpace, outputColorSpace, conversions ); if ( !conversions.size() ) { throw InvalidArgumentException( ( boost::format( "ColorSpaceTransformOp: Cannot find appropriate conversion from '%s' to '%s'" ) % inputColorSpace % outputColorSpace ).str() ); } std::vector< std::string > channelNames; typedef std::vector< std::vector< std::string > > ChannelSets; ChannelSets channelSets; std::vector< std::string > channels; for ( std::vector< std::string >::const_iterator it = channelsParameter()->getTypedValue().begin(); it != channelsParameter()->getTypedValue().end(); ++it ) { const std::string &channelName = *it; channelNames.push_back( channelName ); PrimitiveVariableMap::iterator channelIt = image->variables.find( channelName ); if( channelIt==image->variables.end() || !(channelIt->second.data) ) { throw Exception( str( format( "Channel \"%s\" does not exist." ) % channelName ) ); } TypeId type = channelIt->second.data->typeId(); if ( type == Color3fVectorDataTypeId || type == Color3dVectorDataTypeId ) { // color data types define the three channels. if ( channels.size() ) { /// silently ignores malformed sets. channels.clear(); } channels.push_back( channelName ); channelSets.push_back( channels ); channels.clear(); } else { // simple data types are assigned to one image channel. channels.push_back( channelName ); if ( channels.size() == 3 ) { channelSets.push_back( channels ); channels.clear(); } } } bool first = true; ConversionInfo previous; ConversionInfo current; for( std::vector< ConversionInfo >::const_iterator it = conversions.begin() ; it != conversions.end(); ++it ) { current = *it; if ( first ) { assert( current.get<1>() == inputColorSpace ); first = false; } else { assert( previous.get<2>() == current.get<1>() ); } ModifyOpPtr currentConversion = (*current.get<0>())( current.get<1>(), current.get<2>() ); assert( currentConversion ); if ( !currentConversion->isInstanceOf( ChannelOpTypeId ) && !currentConversion->isInstanceOf( ColorTransformOpTypeId ) ) { throw InvalidArgumentException( ( boost::format( "ColorSpaceTransformOp: '%s' to '%s' conversion registered unsupported Op type '%s'" ) % inputColorSpace % outputColorSpace % currentConversion->typeName()).str() ); } currentConversion->inputParameter()->setValue( image ); currentConversion->copyParameter()->setTypedValue( false ); ImagePrimitivePtr result = 0; if ( currentConversion->isInstanceOf( ChannelOpTypeId ) ) { // The channel Op doesn't handle any unpremultiplication of the colour channels. // So we apply an unpremult before and a premult after, if premultiplied is on and alpha exists if( premultipliedParameter()->getTypedValue() ) { std::string alphaPrimVar = alphaPrimVarParameter()->getTypedValue(); if( image->variables.find( alphaPrimVar ) != image->variables.end() ) { ImageUnpremultiplyOpPtr unpremultOp = new ImageUnpremultiplyOp(); unpremultOp->alphaChannelNameParameter()->setTypedValue( alphaPrimVar ); unpremultOp->channelNamesParameter()->setTypedValue( channelNames ); unpremultOp->copyParameter()->setTypedValue( false ); unpremultOp->inputParameter()->setValue( image ); unpremultOp->operate(); } } ChannelOpPtr op = assertedStaticCast< ChannelOp >( currentConversion ); op->channelNamesParameter()->setTypedValue( channelNames ); result = runTimeCast< ImagePrimitive >( op->operate() ); if( premultipliedParameter()->getTypedValue() ) { std::string alphaPrimVar = alphaPrimVarParameter()->getTypedValue(); if( result->variables.find( alphaPrimVar ) != result->variables.end() ) { ImagePremultiplyOpPtr premultOp = new ImagePremultiplyOp(); premultOp->alphaChannelNameParameter()->setTypedValue( alphaPrimVar ); premultOp->channelNamesParameter()->setTypedValue( channelNames ); premultOp->copyParameter()->setTypedValue( false ); premultOp->inputParameter()->setValue( result ); premultOp->operate(); } } } else { assert( currentConversion->isInstanceOf( ColorTransformOpTypeId ) ); ColorTransformOpPtr op = boost::dynamic_pointer_cast< ColorTransformOp >( currentConversion ); for ( ChannelSets::const_iterator it = channelSets.begin(); it != channelSets.end(); ++it ) { op->inputParameter()->setValue( image ); op->copyParameter()->setTypedValue( false ); op->alphaPrimVarParameter()->setValue( alphaPrimVarParameter()->getValue() ); op->premultipliedParameter()->setValue( premultipliedParameter()->getValue() ); if ( it->size() == 1 ) { op->colorPrimVarParameter()->setTypedValue( (*it)[0] ); op->redPrimVarParameter()->setValue( op->redPrimVarParameter()->defaultValue()->copy() ); op->greenPrimVarParameter()->setValue( op->greenPrimVarParameter()->defaultValue()->copy() ); op->bluePrimVarParameter()->setValue( op->bluePrimVarParameter()->defaultValue()->copy() ); } else { assert( it->size() == 3 ); op->redPrimVarParameter()->setTypedValue( (*it)[0] ); op->greenPrimVarParameter()->setTypedValue( (*it)[1] ); op->bluePrimVarParameter()->setTypedValue( (*it)[2] ); op->colorPrimVarParameter()->setValue( op->colorPrimVarParameter()->defaultValue()->copy() ); } result = runTimeCast< ImagePrimitive >( op->operate() ); assert( result.get() == image ); } } assert( result.get() == image ); ( void ) result; previous = current; } assert( current.get<2>() == outputColorSpace ); }
// static function used by the cache mechanism to actually load the object data from file. static ObjectPtr computeFn( const ComputeParameters ¶ms ) { const std::string &filePath = params.first; MemberData *data = params.second; { /// Check if the file failed before... FileErrors::const_accessor cit; if ( data->m_fileErrors.find( cit, filePath ) ) { throw Exception( ( format( "Previous attempt to read %s failed: %s" ) % filePath % cit->second ).str() ); } } ObjectPtr result(0); try { path resolvedPath = data->m_searchPaths.find( filePath ); if( resolvedPath.empty() ) { string pathList; for( list<path>::const_iterator it = data->m_searchPaths.paths.begin(); it!= data->m_searchPaths.paths.end(); it++ ) { if ( pathList.size() > 0 ) { pathList += ":" + it->string(); } else { pathList = it->string(); } } throw Exception( "Could not find file '" + filePath + "' at the following paths: " + pathList ); } ReaderPtr r = Reader::create( resolvedPath.string() ); if( !r ) { throw Exception( "Could not create reader for '" + resolvedPath.string() + "'" ); } result = r->read(); /// \todo Why would this ever be NULL? Wouldn't we have thrown an exception already if /// we were unable to read the file? if( !result ) { throw Exception( "Reader for '" + resolvedPath.string() + "' returned no data" ); } if( data->m_postProcessor ) { /// \todo We need to allow arguments to be passed to Op::operate() directly /// so that the same Op can be used from multiple threads with different arguments. /// This means adding an overloaded operate() method but more importantly making sure /// that all Ops only use their operands to access arguments and not go getting them /// from the Parameters directly. tbb::mutex::scoped_lock l( data->m_postProcessorMutex ); ModifyOpPtr postProcessor = constPointerCast<ModifyOp>( data->m_postProcessor ); postProcessor->inputParameter()->setValue( result ); postProcessor->copyParameter()->setTypedValue( false ); postProcessor->operate(); } } catch ( std::exception &e ) { data->registerFileError( filePath, e.what() ); throw; } catch ( ... ) { data->registerFileError( filePath, "Unexpected error." ); throw; } return result; }