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 ); }
void ImageCompositeOp::composite( CompositeFn fn, DataWindowResult dwr, ImagePrimitive * imageB, const CompoundObject * operands ) { assert( fn ); assert( imageB ); assert( operands ); const StringVectorParameter::ValueType &channelNames = channelNamesParameter()->getTypedValue(); if ( !channelNames.size() ) { throw InvalidArgumentException( "ImageCompositeOp: No channels specified" ); } ImagePrimitivePtr imageA = runTimeCast< ImagePrimitive > ( imageAParameter()->getValue() ); assert( imageA ); const std::string &alphaChannel = alphaChannelNameParameter()->getTypedValue(); if ( !imageA->arePrimitiveVariablesValid() ) { throw InvalidArgumentException( "ImageCompositeOp: Input image has invalid channels" ); } const int inputMode = m_inputModeParameter->getNumericValue(); if ( inputMode == Unpremultiplied ) { ImagePremultiplyOpPtr premultOp = new ImagePremultiplyOp(); premultOp->alphaChannelNameParameter()->setTypedValue( alphaChannel ); premultOp->channelNamesParameter()->setTypedValue( channelNames ); if ( imageA->variables.find( alphaChannel ) != imageA->variables.end() ) { /// Make a new imageA, premultiplied premultOp->copyParameter()->setTypedValue( true ); premultOp->inputParameter()->setValue( imageA ); imageA = assertedStaticCast< ImagePrimitive >( premultOp->operate() ); assert( imageA->arePrimitiveVariablesValid() ); } if ( imageB->variables.find( alphaChannel ) != imageB->variables.end() ) { /// Premultiply imageB in-place premultOp->copyParameter()->setTypedValue( false ); premultOp->inputParameter()->setValue( imageB ); premultOp->operate(); } } else { assert( inputMode == Premultiplied ); } const Imath::Box2i displayWindow = imageB->getDisplayWindow(); Imath::Box2i newDataWindow = imageB->getDataWindow(); if ( dwr == Union ) { newDataWindow.extendBy( imageA->getDataWindow() ); } else { assert( dwr == Intersection ); newDataWindow = boxIntersection( newDataWindow, imageA->getDataWindow() ); } newDataWindow = boxIntersection( newDataWindow, displayWindow ); ImageCropOpPtr cropOp = new ImageCropOp(); /// Need to make sure that we don't create a new image here - we want to modify the current one in-place. So, /// we turn off the "copy" parameter of ModifyOp. cropOp->copyParameter()->setTypedValue( false ); cropOp->inputParameter()->setValue( imageB ); cropOp->cropBoxParameter()->setTypedValue( newDataWindow ); cropOp->matchDataWindowParameter()->setTypedValue( true ); cropOp->resetOriginParameter()->setTypedValue( false ); cropOp->operate(); assert( imageB->arePrimitiveVariablesValid() ); assert( imageB->getDataWindow() == newDataWindow ); /// \todo Use the "reformat" parameter of the ImageCropOp to do this, when it's implemented imageB->setDisplayWindow( displayWindow ); FloatVectorDataPtr aAlphaData = getChannelData( imageA.get(), alphaChannel, false ); FloatVectorDataPtr bAlphaData = getChannelData( imageB, alphaChannel, false ); const int newWidth = newDataWindow.size().x + 1; const int newHeight = newDataWindow.size().y + 1; const int newArea = newWidth * newHeight; assert( newArea == (int)imageB->variableSize( PrimitiveVariable::Vertex ) ); for( unsigned i=0; i<channelNames.size(); i++ ) { const StringVectorParameter::ValueType::value_type &channelName = channelNames[i]; FloatVectorDataPtr aData = getChannelData( imageA.get(), channelName ); assert( aData->readable().size() == imageA->variableSize( PrimitiveVariable::Vertex ) ); FloatVectorDataPtr bData = getChannelData( imageB, channelName ); assert( bData->readable().size() == imageB->variableSize( PrimitiveVariable::Vertex ) ); FloatVectorDataPtr newBData = new FloatVectorData(); newBData->writable().resize( newArea ); imageB->variables[ channelName ].data = newBData; for ( int y = newDataWindow.min.y; y <= newDataWindow.max.y; y++ ) { int offset = (y - newDataWindow.min.y ) * newWidth; for ( int x = newDataWindow.min.x; x <= newDataWindow.max.x; x++, offset++ ) { float aVal = readChannelData( imageA.get(), aData.get(), V2i( x, y ) ); float bVal = readChannelData( imageB, bData.get(), V2i( x, y ) ); float aAlpha = aAlphaData ? readChannelData( imageA.get(), aAlphaData.get(), V2i( x, y ) ) : 1.0f; float bAlpha = bAlphaData ? readChannelData( imageB, bAlphaData.get(), V2i( x, y ) ) : 1.0f; assert( offset >= 0 ); assert( offset < (int)newBData->readable().size() ); newBData->writable()[ offset ] = fn( aVal, aAlpha, bVal, bAlpha ); } } } /// displayWindow should be unchanged assert( imageB->getDisplayWindow() == displayWindow ); assert( imageB->arePrimitiveVariablesValid() ); /// If input images were unpremultiplied, then ensure that the output is also if ( inputMode == Unpremultiplied && imageB->variables.find( alphaChannel ) != imageB->variables.end() ) { ImageUnpremultiplyOpPtr unpremultOp = new ImageUnpremultiplyOp(); /// Unpremultiply imageB in-place unpremultOp->copyParameter()->setTypedValue( false ); unpremultOp->channelNamesParameter()->setTypedValue( channelNames ); unpremultOp->alphaChannelNameParameter()->setTypedValue( alphaChannel ); unpremultOp->inputParameter()->setValue( imageB ); unpremultOp->operate(); assert( imageB->arePrimitiveVariablesValid() ); } else { assert( inputMode == Premultiplied ); } }