diff options
author | Matteo F. Vescovi <mfv.debian@gmail.com> | 2013-08-20 09:53:19 +0100 |
---|---|---|
committer | Matteo F. Vescovi <mfv.debian@gmail.com> | 2013-08-20 09:53:19 +0100 |
commit | 66e5d9e2915733247bca47d077414ec2594aedad (patch) | |
tree | f4070a31bf015e159dadd34378cda703d8f6edea /src/nuke/OCIOColorSpace |
opencolorio (1.0.8~dfsg0-2) unstable; urgency=low
* debian/rules: get-orig-source stuff added
* debian/rules: useless dh addon removed
* debian/rules: License.txt duplicate removed
* debian/rules: SSE optimization disabled (Closes: #719174)
* debian/libopencolorio1.symbols: file removed (Closes: #719175)
# imported from the archive
Diffstat (limited to 'src/nuke/OCIOColorSpace')
-rw-r--r-- | src/nuke/OCIOColorSpace/OCIOColorSpace.cpp | 376 | ||||
-rw-r--r-- | src/nuke/OCIOColorSpace/OCIOColorSpace.h | 120 |
2 files changed, 496 insertions, 0 deletions
diff --git a/src/nuke/OCIOColorSpace/OCIOColorSpace.cpp b/src/nuke/OCIOColorSpace/OCIOColorSpace.cpp new file mode 100644 index 0000000..091c1ef --- /dev/null +++ b/src/nuke/OCIOColorSpace/OCIOColorSpace.cpp @@ -0,0 +1,376 @@ +/** + * OpenColorIO ColorSpace Iop. + */ + +#include "OCIOColorSpace.h" + +namespace OCIO = OCIO_NAMESPACE; + +#include <string> +#include <sstream> +#include <stdexcept> + +#include <DDImage/Channel.h> +#include <DDImage/PixelIop.h> +#include <DDImage/NukeWrapper.h> +#include <DDImage/Row.h> +#include <DDImage/Knobs.h> +#include <DDImage/ddImageVersionNumbers.h> + +// Should we use cascasing ColorSpace menus +#if defined kDDImageVersionInteger && (kDDImageVersionInteger>=62300) +#define OCIO_CASCADE +#endif + +OCIOColorSpace::OCIOColorSpace(Node *n) : DD::Image::PixelIop(n) +{ + m_hasColorSpaces = false; + + m_inputColorSpaceIndex = 0; + m_outputColorSpaceIndex = 0; + + // Query the color space names from the current config + // TODO (when to) re-grab the list of available color spaces? How to save/load? + + try + { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + OCIO::ConstColorSpaceRcPtr defaultcs = config->getColorSpace(OCIO::ROLE_SCENE_LINEAR); + if(!defaultcs) throw std::runtime_error("ROLE_SCENE_LINEAR not defined."); + std::string defaultColorSpaceName = defaultcs->getName(); + + for(int i = 0; i < config->getNumColorSpaces(); i++) + { + std::string csname = config->getColorSpaceNameByIndex(i); + +#ifdef OCIO_CASCADE + std::string family = config->getColorSpace(csname.c_str())->getFamily(); + if(family.empty()) + m_colorSpaceNames.push_back(csname.c_str()); + else + m_colorSpaceNames.push_back(family + "/" + csname); +#else + m_colorSpaceNames.push_back(csname); +#endif + + if(csname == defaultColorSpaceName) + { + m_inputColorSpaceIndex = i; + m_outputColorSpaceIndex = i; + } + } + } + catch (OCIO::Exception& e) + { + std::cerr << "OCIOColorSpace: " << e.what() << std::endl; + } + catch (...) + { + std::cerr << "OCIOColorSpace: Unknown exception during OCIO setup." << std::endl; + } + + // Then, create a cstr array for passing to Nuke + // This must be done in a second pass, lest the original m_colorSpaceNames + // std::string be reallocated in the interim + for(unsigned int i=0; i<m_colorSpaceNames.size(); ++i) + { + m_inputColorSpaceCstrNames.push_back(m_colorSpaceNames[i].c_str()); + m_outputColorSpaceCstrNames.push_back(m_colorSpaceNames[i].c_str()); + } + + m_inputColorSpaceCstrNames.push_back(NULL); + m_outputColorSpaceCstrNames.push_back(NULL); + + m_hasColorSpaces = (!m_colorSpaceNames.empty()); + + if(!m_hasColorSpaces) + { + std::cerr << "OCIOColorSpace: No color spaces available for input and/or output." << std::endl; + } +} + +OCIOColorSpace::~OCIOColorSpace() +{ + +} + +void OCIOColorSpace::knobs(DD::Image::Knob_Callback f) +{ +#ifdef OCIO_CASCADE + DD::Image::CascadingEnumeration_knob(f, + &m_inputColorSpaceIndex, &m_inputColorSpaceCstrNames[0], "in_colorspace", "in"); + DD::Image::Tooltip(f, "Input data is taken to be in this color space."); + DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE); + + DD::Image::CascadingEnumeration_knob(f, + &m_outputColorSpaceIndex, &m_outputColorSpaceCstrNames[0], "out_colorspace", "out"); + DD::Image::Tooltip(f, "Image data is converted to this color space for output."); + DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE); +#else + DD::Image::Enumeration_knob(f, + &m_inputColorSpaceIndex, &m_inputColorSpaceCstrNames[0], "in_colorspace", "in"); + DD::Image::Tooltip(f, "Input data is taken to be in this color space."); + DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE); + + DD::Image::Enumeration_knob(f, + &m_outputColorSpaceIndex, &m_outputColorSpaceCstrNames[0], "out_colorspace", "out"); + DD::Image::Tooltip(f, "Image data is converted to this color space for output."); + DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE); +#endif + +} + +OCIO::ConstContextRcPtr OCIOColorSpace::getLocalContext() +{ + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + OCIO::ConstContextRcPtr context = config->getCurrentContext(); + OCIO::ContextRcPtr mutableContext; + + if(!m_contextKey1.empty()) + { + if(!mutableContext) mutableContext = context->createEditableCopy(); + mutableContext->setStringVar(m_contextKey1.c_str(), m_contextValue1.c_str()); + } + if(!m_contextKey2.empty()) + { + if(!mutableContext) mutableContext = context->createEditableCopy(); + mutableContext->setStringVar(m_contextKey2.c_str(), m_contextValue2.c_str()); + } + if(!m_contextKey3.empty()) + { + if(!mutableContext) mutableContext = context->createEditableCopy(); + mutableContext->setStringVar(m_contextKey3.c_str(), m_contextValue3.c_str()); + } + if(!m_contextKey4.empty()) + { + if(!mutableContext) mutableContext = context->createEditableCopy(); + mutableContext->setStringVar(m_contextKey4.c_str(), m_contextValue4.c_str()); + } + + if(mutableContext) context = mutableContext; + return context; +} + +void OCIOColorSpace::append(DD::Image::Hash& localhash) +{ + // TODO: Hang onto the context, what if getting it + // (and querying getCacheID) is expensive? + try + { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + OCIO::ConstContextRcPtr context = getLocalContext(); + std::string configCacheID = config->getCacheID(context); + localhash.append(configCacheID); + } + catch(OCIO::Exception &e) + { + error(e.what()); + return; + } +} + +void OCIOColorSpace::_validate(bool for_real) +{ + if(!m_hasColorSpaces) + { + error("No color spaces available for input and/or output."); + return; + } + + int inputColorSpaceCount = static_cast<int>(m_inputColorSpaceCstrNames.size()) - 1; + if(m_inputColorSpaceIndex < 0 || m_inputColorSpaceIndex >= inputColorSpaceCount) + { + std::ostringstream err; + err << "Input color space index (" << m_inputColorSpaceIndex << ") out of range."; + error(err.str().c_str()); + return; + } + + int outputColorSpaceCount = static_cast<int>(m_outputColorSpaceCstrNames.size()) - 1; + if(m_outputColorSpaceIndex < 0 || m_outputColorSpaceIndex >= outputColorSpaceCount) + { + std::ostringstream err; + err << "Output color space index (" << m_outputColorSpaceIndex << ") out of range."; + error(err.str().c_str()); + return; + } + + try + { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + const char * inputName = config->getColorSpaceNameByIndex(m_inputColorSpaceIndex); + const char * outputName = config->getColorSpaceNameByIndex(m_outputColorSpaceIndex); + + OCIO::ConstContextRcPtr context = getLocalContext(); + m_processor = config->getProcessor(context, inputName, outputName); + } + catch(OCIO::Exception &e) + { + error(e.what()); + return; + } + + if(m_processor->isNoOp()) + { + set_out_channels(DD::Image::Mask_None); // prevents engine() from being called + } else { + set_out_channels(DD::Image::Mask_All); + } + + DD::Image::PixelIop::_validate(for_real); +} + +// Note that this is copied by others (OCIODisplay) +void OCIOColorSpace::in_channels(int /* n unused */, DD::Image::ChannelSet& mask) const +{ + DD::Image::ChannelSet done; + foreach(c, mask) + { + if (DD::Image::colourIndex(c) < 3 && !(done & c)) + { + done.addBrothers(c, 3); + } + } + mask += done; +} + +// See Saturation::pixel_engine for a well-commented example. +// Note that this is copied by others (OCIODisplay) +void OCIOColorSpace::pixel_engine( + const DD::Image::Row& in, + int /* rowY unused */, int rowX, int rowXBound, + DD::Image::ChannelMask outputChannels, + DD::Image::Row& out) +{ + int rowWidth = rowXBound - rowX; + + DD::Image::ChannelSet done; + foreach (requestedChannel, outputChannels) + { + // Skip channels which had their trios processed already, + if (done & requestedChannel) + { + continue; + } + + // Pass through channels which are not selected for processing + // and non-rgb channels. + if (colourIndex(requestedChannel) >= 3) + { + out.copy(in, requestedChannel, rowX, rowXBound); + continue; + } + + DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0); + DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1); + DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2); + + done += rChannel; + done += gChannel; + done += bChannel; + + const float *rIn = in[rChannel] + rowX; + const float *gIn = in[gChannel] + rowX; + const float *bIn = in[bChannel] + rowX; + + float *rOut = out.writable(rChannel) + rowX; + float *gOut = out.writable(gChannel) + rowX; + float *bOut = out.writable(bChannel) + rowX; + + // OCIO modifies in-place + // Note: xOut can equal xIn in some circumstances, such as when the + // 'Black' (throwaway) scanline is uses. We thus must guard memcpy, + // which does not allow for overlapping regions. + if (rOut != rIn) memcpy(rOut, rIn, sizeof(float)*rowWidth); + if (gOut != gIn) memcpy(gOut, gIn, sizeof(float)*rowWidth); + if (bOut != bIn) memcpy(bOut, bIn, sizeof(float)*rowWidth); + + try + { + OCIO::PlanarImageDesc img(rOut, gOut, bOut, NULL, rowWidth, /*height*/ 1); + m_processor->apply(img); + } + catch(OCIO::Exception &e) + { + error(e.what()); + } + } +} + +const DD::Image::Op::Description OCIOColorSpace::description("OCIOColorSpace", build); + +const char* OCIOColorSpace::Class() const +{ + return description.name; +} + +const char* OCIOColorSpace::displayName() const +{ + return description.name; +} + +const char* OCIOColorSpace::node_help() const +{ + // TODO more detailed help text + return "Use OpenColorIO to convert from one color space to another."; +} + +// This class is necessary in order to call knobsAtTheEnd(). Otherwise, the NukeWrapper knobs +// will be added to the Context tab instead of the primary tab. +class OCIOColorSpaceNukeWrapper : public DD::Image::NukeWrapper +{ +public: + OCIOColorSpaceNukeWrapper(DD::Image::PixelIop* op) : DD::Image::NukeWrapper(op) + { + } + + virtual void attach() + { + wrapped_iop()->attach(); + } + + virtual void detach() + { + wrapped_iop()->detach(); + } + + virtual void knobs(DD::Image::Knob_Callback f) + { + OCIOColorSpace* csIop = dynamic_cast<OCIOColorSpace*>(wrapped_iop()); + if(!csIop) return; + + DD::Image::NukeWrapper::knobs(f); + + DD::Image::Tab_knob(f, "Context"); + { + DD::Image::String_knob(f, &csIop->m_contextKey1, "key1"); + DD::Image::Spacer(f, 10); + DD::Image::String_knob(f, &csIop->m_contextValue1, "value1"); + DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE); + + DD::Image::String_knob(f, &csIop->m_contextKey2, "key2"); + DD::Image::Spacer(f, 10); + DD::Image::String_knob(f, &csIop->m_contextValue2, "value2"); + DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE); + + DD::Image::String_knob(f, &csIop->m_contextKey3, "key3"); + DD::Image::Spacer(f, 10); + DD::Image::String_knob(f, &csIop->m_contextValue3, "value3"); + DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE); + + DD::Image::String_knob(f, &csIop->m_contextKey4, "key4"); + DD::Image::Spacer(f, 10); + DD::Image::String_knob(f, &csIop->m_contextValue4, "value4"); + DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE); + } + } +}; + +static DD::Image::Op* build(Node *node) +{ + DD::Image::NukeWrapper *op = (new OCIOColorSpaceNukeWrapper(new OCIOColorSpace(node))); + op->channels(DD::Image::Mask_RGB); + return op; +} diff --git a/src/nuke/OCIOColorSpace/OCIOColorSpace.h b/src/nuke/OCIOColorSpace/OCIOColorSpace.h new file mode 100644 index 0000000..12b243b --- /dev/null +++ b/src/nuke/OCIOColorSpace/OCIOColorSpace.h @@ -0,0 +1,120 @@ +#ifndef INCLUDED_OCIO_NUKE_COLORSPACECONVERSION_H_ +#define INCLUDED_OCIO_NUKE_COLORSPACECONVERSION_H_ + +// Include these early, for Nuke's headers under gcc 4.4.2. +#include <memory> +#include <cstdarg> + +#include <DDImage/PixelIop.h> +#include <DDImage/Row.h> +#include <DDImage/Knob.h> + +#include <OpenColorIO/OpenColorIO.h> +namespace OCIO = OCIO_NAMESPACE; + + +/*! + * Iop that uses OpenColorIO to perform colorspace conversions + */ +class OCIOColorSpace : public DD::Image::PixelIop { + + protected: + + bool m_hasColorSpaces; //!< Were colorspaces found for both input and output? If not, always error. + int m_inputColorSpaceIndex; //!< index of input colorspace selection from the pulldown list knob + int m_outputColorSpaceIndex; + std::vector<std::string> m_colorSpaceNames; //!< list of input and output colorspace names (memory for const char* s below) + std::vector<const char*> m_inputColorSpaceCstrNames; //!< list for the pulldown list knob (used raw) + std::vector<const char*> m_outputColorSpaceCstrNames; + + OCIO::ConstContextRcPtr getLocalContext(); + + OCIO::ConstProcessorRcPtr m_processor; + public: + + OCIOColorSpace(Node *node); + + virtual ~OCIOColorSpace(); + + // These are public so the nuke wrapper can introspect into it + // TODO: use 'friend' instead + std::string m_contextKey1; + std::string m_contextValue1; + std::string m_contextKey2; + std::string m_contextValue2; + std::string m_contextKey3; + std::string m_contextValue3; + std::string m_contextKey4; + std::string m_contextValue4; + + static const DD::Image::Op::Description description; + + /*! Return the command name that will be stored in Nuke scripts. */ + virtual const char *Class() const; + + /*! + * Return a name for this class that will be shown to the user. The + * default implementation returns Class(). You can return a different + * (ie more user-friendly) name instead here, and there is no need for + * this to be unique. + * + * Nuke currently will remove any trailing digits and underscores from + * this and add a new number to make a unique name for the new node. + * + * \return "OCIOColorSpace" + */ + virtual const char *displayName() const; + + /*! + * Return help information for this node. This information is in the + * pop-up window that the user gets when they hit the [?] button in + * the lower-left corner of the control panel. + */ + virtual const char *node_help() const; + + /*! + * Define the knobs that will be presented in the control panel. + */ + virtual void knobs(DD::Image::Knob_Callback f); + + /*! + * Specify the channels required from input n to produce the channels + * in mask by modifying mask in-place. (At least one channel in the + * input is assumed.) + * + * Since colorspace conversions can have channel cross-talk, any rgb + * output channel requires all its rgb bretheren. (Non-rgb + * are passed through.) + */ + virtual void in_channels(int n, DD::Image::ChannelSet& mask) const; + + /*! + * Calculate the output pixel data. + * \param rowY vertical line number + * \param rowX inclusive left bound + * \param rowXBound exclusive right bound + * \param outputChannels a subset of out_channels(), the required channels to be produced + */ + virtual void pixel_engine( + const DD::Image::Row& in, + int rowY, int rowX, int rowXBound, + DD::Image::ChannelMask outputChannels, + DD::Image::Row& out); + + virtual void append(DD::Image::Hash& hash); + + protected: + + /*! + * Check that colorspaces are available, and that the transform + * is not a noop. (As OCIO whether a given transform is a noop, since it + * can do more analysis than just name matching.) + */ + virtual void _validate(bool for_real); + +}; + + +static DD::Image::Op* build(Node *node); + +#endif // INCLUDED_OCIO_NUKE_COLORSPACECONVERSION_H_ |