Skip to content

Commit

Permalink
Added colourspace management to the image viewer.
Browse files Browse the repository at this point in the history
The ImageView class allows arbitrary ImageProcessors to be registered as colour transforms, and chosen using a new "displayTransform" plug. The ocio.py config file registers a processor for each display transform in the OpenColorIO config, and a Default transform which tracks the global display transform specified via the preferences.

Fixes GafferHQ#573.
  • Loading branch information
johnhaddon committed Nov 8, 2013
1 parent 8b90c54 commit 7b0baf6
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 2 deletions.
20 changes: 20 additions & 0 deletions include/GafferImageUI/ImageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
namespace GafferImage
{

IE_CORE_FORWARDDECLARE( ImageProcessor )
IE_CORE_FORWARDDECLARE( Grade )
IE_CORE_FORWARDDECLARE( ImageStats )
IE_CORE_FORWARDDECLARE( ImagePlug )
Expand All @@ -73,6 +74,15 @@ class ImageView : public GafferUI::View
Gaffer::FloatPlug *gammaPlug();
const Gaffer::FloatPlug *gammaPlug() const;

/// Values should be names that exist in registeredDisplayTransforms().
Gaffer::StringPlug *displayTransformPlug();
const Gaffer::StringPlug *displayTransformPlug() const;

typedef boost::function<GafferImage::ImageProcessorPtr ()> DisplayTransformCreator;

static void registerDisplayTransform( const std::string &name, DisplayTransformCreator creator );
static void registeredDisplayTransforms( std::vector<std::string> &names );

protected :

/// May be called from a subclass constructor to add a converter
Expand All @@ -95,7 +105,14 @@ class ImageView : public GafferUI::View
GafferImage::Grade *gradeNode();
const GafferImage::Grade *gradeNode() const;

GafferImage::ImageProcessor *displayTransformNode();
const GafferImage::ImageProcessor *displayTransformNode() const;

void plugSet( Gaffer::Plug *plug );
void insertDisplayTransform();

typedef std::map<std::string, GafferImage::ImageProcessorPtr> DisplayTransformMap;
DisplayTransformMap m_displayTransforms;

int m_channelToView;
Imath::V2f m_mousePos;
Expand All @@ -104,6 +121,9 @@ class ImageView : public GafferUI::View
Imath::Color4f m_maxColor;
Imath::Color4f m_averageColor;

typedef std::map<std::string, DisplayTransformCreator> DisplayTransformCreatorMap;
static DisplayTransformCreatorMap &displayTransformCreators();

static ViewDescription<ImageView> g_viewDescription;

friend void GafferImageUIBindings::bindImageView();
Expand Down
87 changes: 85 additions & 2 deletions src/GafferImageUI/ImageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ class ImageViewGadget : public GafferUI::Gadget
"void main()"
"{"
" OUTCOLOR = texture2D( texture, gl_TexCoord[0].xy );"
" OUTCOLOR = vec4( ieLinToSRGB( OUTCOLOR.r ), ieLinToSRGB( OUTCOLOR.g ), ieLinToSRGB( OUTCOLOR.b ), ieLinToSRGB( OUTCOLOR.a ) );"
" if( channelToView==1 )"
" {"
" OUTCOLOR = vec4( OUTCOLOR[0], OUTCOLOR[0], OUTCOLOR[0], 1. );"
Expand Down Expand Up @@ -955,6 +954,8 @@ ImageView::ImageView( const std::string &name )
gradeNode->gammaPlug()->getChild( 1 )->setInput( gammaPlug );
gradeNode->gammaPlug()->getChild( 2 )->setInput( gammaPlug );

addChild( new StringPlug( "displayTransform", Plug::In, "", Plug::Default & ~Plug::AcceptsInputs ) );

ImagePlugPtr preprocessorOutput = new ImagePlug( "out", Plug::Out );
preprocessor->addChild( preprocessorOutput );
preprocessorOutput->setInput( gradeNode->outPlug() );
Expand All @@ -967,6 +968,9 @@ ImageView::ImageView( const std::string &name )

plugSetSignal().connect( boost::bind( &ImageView::plugSet, this, ::_1 ) );

// trigger insertDisplayTransform()

displayTransformPlug()->setValue( "Default" );
}

void ImageView::insertConverter( Gaffer::NodePtr converter )
Expand Down Expand Up @@ -1024,7 +1028,17 @@ const Gaffer::FloatPlug *ImageView::gammaPlug() const
{
return getChild<FloatPlug>( "gamma" );
}


Gaffer::StringPlug *ImageView::displayTransformPlug()
{
return getChild<StringPlug>( "displayTransform" );
}

const Gaffer::StringPlug *ImageView::displayTransformPlug() const
{
return getChild<StringPlug>( "displayTransform" );
}

GafferImage::ImageStats *ImageView::imageStatsNode()
{
return getChild<ImageStats>( "__imageStats" );
Expand Down Expand Up @@ -1100,5 +1114,74 @@ void ImageView::plugSet( Gaffer::Plug *plug )
g->multiplyPlug()->setValue( Color3f( m ) );
}
}
else if( plug == displayTransformPlug() )
{
insertDisplayTransform();
}
}

void ImageView::insertDisplayTransform()
{
Grade *grade = gradeNode();
if( !grade )
{
/// \todo Remove this guard when we know no
/// subclasses are calling setPreprocessor.
return;
}

const std::string name = displayTransformPlug()->getValue();

ImageProcessorPtr displayTransform;
DisplayTransformMap::const_iterator it = m_displayTransforms.find( name );
if( it != m_displayTransforms.end() )
{
displayTransform = it->second;
}
else
{
DisplayTransformCreatorMap &m = displayTransformCreators();
DisplayTransformCreatorMap::const_iterator it = m.find( displayTransformPlug()->getValue() );
if( it != m.end() )
{
displayTransform = it->second();
}
if( displayTransform )
{
m_displayTransforms[name] = displayTransform;
displayTransform->setName( name );
getPreprocessor<Node>()->addChild( displayTransform );
}
}

if( displayTransform )
{
displayTransform->inPlug()->setInput( grade->outPlug() );
getPreprocessor<Node>()->getChild<Plug>( "out" )->setInput( displayTransform->outPlug() );
}
else
{
getPreprocessor<Node>()->getChild<Plug>( "out" )->setInput( grade->outPlug() );
}
}

void ImageView::registerDisplayTransform( const std::string &name, DisplayTransformCreator creator )
{
displayTransformCreators()[name] = creator;
}

void ImageView::registeredDisplayTransforms( std::vector<std::string> &names )
{
const DisplayTransformCreatorMap &m = displayTransformCreators();
names.clear();
for( DisplayTransformCreatorMap::const_iterator it = m.begin(), eIt = m.end(); it != eIt; ++it )
{
names.push_back( it->first );
}
}

ImageView::DisplayTransformCreatorMap &ImageView::displayTransformCreators()
{
static DisplayTransformCreatorMap g_creators;
return g_creators;
}
46 changes: 46 additions & 0 deletions src/GafferImageUIBindings/ImageViewBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@

#include "GafferBindings/NodeBinding.h"

#include "GafferImage/ImageProcessor.h"

#include "GafferImageUI/ImageView.h"
#include "GafferImageUIBindings/ImageViewBinding.h"

using namespace std;
using namespace boost::python;
using namespace Gaffer;
using namespace GafferBindings;
using namespace GafferImage;
using namespace GafferImageUI;

class ImageViewWrapper : public NodeWrapper<ImageView>
Expand All @@ -65,13 +69,55 @@ class ImageViewWrapper : public NodeWrapper<ImageView>

IE_CORE_DECLAREPTR( ImageViewWrapper );

struct DisplayTransformCreator
{
DisplayTransformCreator( object fn )
: m_fn( fn )
{
}

ImageProcessorPtr operator()()
{
IECorePython::ScopedGILLock gilLock;
ImageProcessorPtr result = extract<ImageProcessorPtr>( m_fn() );
return result;
}

private :

object m_fn;

};

static void registerDisplayTransform( const std::string &name, object creator )
{
ImageView::registerDisplayTransform( name, DisplayTransformCreator( creator ) );
}

static boost::python::list registeredDisplayTransforms()
{
vector<string> n;
ImageView::registeredDisplayTransforms( n );
boost::python::list result;
for( vector<string>::const_iterator it = n.begin(), eIt = n.end(); it != eIt; ++it )
{
result.append( *it );
}

return result;
}

void GafferImageUIBindings::bindImageView()
{

GafferBindings::NodeClass<ImageView, ImageViewWrapperPtr>()
/// \todo Remove deprecated input argument.
.def( init<const std::string &, Gaffer::PlugPtr>() )
.def( "_insertConverter", &ImageView::insertConverter )
.def( "registerDisplayTransform", &registerDisplayTransform )
.staticmethod( "registerDisplayTransform" )
.def( "registeredDisplayTransforms", &registeredDisplayTransforms )
.staticmethod( "registeredDisplayTransforms" )
;

}
39 changes: 39 additions & 0 deletions startup/gui/ocio.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

import Gaffer
import GafferUI
import GafferImageUI

# get default display setup

Expand Down Expand Up @@ -86,5 +87,43 @@ def __plugSet( plug ) :
return

__setDisplayTransform()
__updateDefaultDisplayTransforms()

application.__ocioPlugSetConnection = preferences.plugSetSignal().connect( __plugSet )

# register display transforms with the image viewer

def __displayTransformCreator( name ) :

result = GafferImage.OpenColorIO()
result["inputSpace"].setValue( "linear" )
result["outputSpace"].setValue( config.getDisplayColorSpaceName( defaultDisplay, name ) )

return result

for name in config.getViews( defaultDisplay ) :
GafferImageUI.ImageView.registerDisplayTransform( name, IECore.curry( __displayTransformCreator, name ) )

# and register a special "Default" display transform which tracks the
# global settings from the preferences

__defaultDisplayTransforms = []

def __updateDefaultDisplayTransforms() :

view = preferences["displayColorSpace"]["view"].getValue()
colorSpace = config.getDisplayColorSpaceName( defaultDisplay, view )
for node in __defaultDisplayTransforms :
node["outputSpace"].setValue( colorSpace )

def __defaultDisplayTransformCreator() :

result = GafferImage.OpenColorIO()
result["inputSpace"].setValue( "linear" )

__defaultDisplayTransforms.append( result )
__updateDefaultDisplayTransforms()

return result

GafferImageUI.ImageView.registerDisplayTransform( "Default", __defaultDisplayTransformCreator )

0 comments on commit 7b0baf6

Please sign in to comment.