/src/nuke/OCIOColorSpace/OCIOColorSpace.cpp

http://github.com/imageworks/OpenColorIO · C++ · 376 lines · 293 code · 59 blank · 24 comment · 35 complexity · 29a363e5121d0fc36901be3a1d9058ae MD5 · raw file

  1. /**
  2. * OpenColorIO ColorSpace Iop.
  3. */
  4. #include "OCIOColorSpace.h"
  5. namespace OCIO = OCIO_NAMESPACE;
  6. #include <string>
  7. #include <sstream>
  8. #include <stdexcept>
  9. #include <DDImage/Channel.h>
  10. #include <DDImage/PixelIop.h>
  11. #include <DDImage/NukeWrapper.h>
  12. #include <DDImage/Row.h>
  13. #include <DDImage/Knobs.h>
  14. #include <DDImage/ddImageVersionNumbers.h>
  15. // Should we use cascasing ColorSpace menus
  16. #if defined kDDImageVersionInteger && (kDDImageVersionInteger>=62300)
  17. #define OCIO_CASCADE
  18. #endif
  19. OCIOColorSpace::OCIOColorSpace(Node *n) : DD::Image::PixelIop(n)
  20. {
  21. m_hasColorSpaces = false;
  22. m_inputColorSpaceIndex = 0;
  23. m_outputColorSpaceIndex = 0;
  24. // Query the color space names from the current config
  25. // TODO (when to) re-grab the list of available color spaces? How to save/load?
  26. try
  27. {
  28. OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  29. OCIO::ConstColorSpaceRcPtr defaultcs = config->getColorSpace(OCIO::ROLE_SCENE_LINEAR);
  30. if(!defaultcs) throw std::runtime_error("ROLE_SCENE_LINEAR not defined.");
  31. std::string defaultColorSpaceName = defaultcs->getName();
  32. for(int i = 0; i < config->getNumColorSpaces(); i++)
  33. {
  34. std::string csname = config->getColorSpaceNameByIndex(i);
  35. #ifdef OCIO_CASCADE
  36. std::string family = config->getColorSpace(csname.c_str())->getFamily();
  37. if(family.empty())
  38. m_colorSpaceNames.push_back(csname.c_str());
  39. else
  40. m_colorSpaceNames.push_back(family + "/" + csname);
  41. #else
  42. m_colorSpaceNames.push_back(csname);
  43. #endif
  44. if(csname == defaultColorSpaceName)
  45. {
  46. m_inputColorSpaceIndex = i;
  47. m_outputColorSpaceIndex = i;
  48. }
  49. }
  50. }
  51. catch (OCIO::Exception& e)
  52. {
  53. std::cerr << "OCIOColorSpace: " << e.what() << std::endl;
  54. }
  55. catch (...)
  56. {
  57. std::cerr << "OCIOColorSpace: Unknown exception during OCIO setup." << std::endl;
  58. }
  59. // Then, create a cstr array for passing to Nuke
  60. // This must be done in a second pass, lest the original m_colorSpaceNames
  61. // std::string be reallocated in the interim
  62. for(unsigned int i=0; i<m_colorSpaceNames.size(); ++i)
  63. {
  64. m_inputColorSpaceCstrNames.push_back(m_colorSpaceNames[i].c_str());
  65. m_outputColorSpaceCstrNames.push_back(m_colorSpaceNames[i].c_str());
  66. }
  67. m_inputColorSpaceCstrNames.push_back(NULL);
  68. m_outputColorSpaceCstrNames.push_back(NULL);
  69. m_hasColorSpaces = (!m_colorSpaceNames.empty());
  70. if(!m_hasColorSpaces)
  71. {
  72. std::cerr << "OCIOColorSpace: No color spaces available for input and/or output." << std::endl;
  73. }
  74. }
  75. OCIOColorSpace::~OCIOColorSpace()
  76. {
  77. }
  78. void OCIOColorSpace::knobs(DD::Image::Knob_Callback f)
  79. {
  80. #ifdef OCIO_CASCADE
  81. DD::Image::CascadingEnumeration_knob(f,
  82. &m_inputColorSpaceIndex, &m_inputColorSpaceCstrNames[0], "in_colorspace", "in");
  83. DD::Image::Tooltip(f, "Input data is taken to be in this color space.");
  84. DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE);
  85. DD::Image::CascadingEnumeration_knob(f,
  86. &m_outputColorSpaceIndex, &m_outputColorSpaceCstrNames[0], "out_colorspace", "out");
  87. DD::Image::Tooltip(f, "Image data is converted to this color space for output.");
  88. DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE);
  89. #else
  90. DD::Image::Enumeration_knob(f,
  91. &m_inputColorSpaceIndex, &m_inputColorSpaceCstrNames[0], "in_colorspace", "in");
  92. DD::Image::Tooltip(f, "Input data is taken to be in this color space.");
  93. DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE);
  94. DD::Image::Enumeration_knob(f,
  95. &m_outputColorSpaceIndex, &m_outputColorSpaceCstrNames[0], "out_colorspace", "out");
  96. DD::Image::Tooltip(f, "Image data is converted to this color space for output.");
  97. DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE);
  98. #endif
  99. }
  100. OCIO::ConstContextRcPtr OCIOColorSpace::getLocalContext()
  101. {
  102. OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  103. OCIO::ConstContextRcPtr context = config->getCurrentContext();
  104. OCIO::ContextRcPtr mutableContext;
  105. if(!m_contextKey1.empty())
  106. {
  107. if(!mutableContext) mutableContext = context->createEditableCopy();
  108. mutableContext->setStringVar(m_contextKey1.c_str(), m_contextValue1.c_str());
  109. }
  110. if(!m_contextKey2.empty())
  111. {
  112. if(!mutableContext) mutableContext = context->createEditableCopy();
  113. mutableContext->setStringVar(m_contextKey2.c_str(), m_contextValue2.c_str());
  114. }
  115. if(!m_contextKey3.empty())
  116. {
  117. if(!mutableContext) mutableContext = context->createEditableCopy();
  118. mutableContext->setStringVar(m_contextKey3.c_str(), m_contextValue3.c_str());
  119. }
  120. if(!m_contextKey4.empty())
  121. {
  122. if(!mutableContext) mutableContext = context->createEditableCopy();
  123. mutableContext->setStringVar(m_contextKey4.c_str(), m_contextValue4.c_str());
  124. }
  125. if(mutableContext) context = mutableContext;
  126. return context;
  127. }
  128. void OCIOColorSpace::append(DD::Image::Hash& localhash)
  129. {
  130. // TODO: Hang onto the context, what if getting it
  131. // (and querying getCacheID) is expensive?
  132. try
  133. {
  134. OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  135. OCIO::ConstContextRcPtr context = getLocalContext();
  136. std::string configCacheID = config->getCacheID(context);
  137. localhash.append(configCacheID);
  138. }
  139. catch(OCIO::Exception &e)
  140. {
  141. error(e.what());
  142. return;
  143. }
  144. }
  145. void OCIOColorSpace::_validate(bool for_real)
  146. {
  147. if(!m_hasColorSpaces)
  148. {
  149. error("No color spaces available for input and/or output.");
  150. return;
  151. }
  152. int inputColorSpaceCount = static_cast<int>(m_inputColorSpaceCstrNames.size()) - 1;
  153. if(m_inputColorSpaceIndex < 0 || m_inputColorSpaceIndex >= inputColorSpaceCount)
  154. {
  155. std::ostringstream err;
  156. err << "Input color space index (" << m_inputColorSpaceIndex << ") out of range.";
  157. error(err.str().c_str());
  158. return;
  159. }
  160. int outputColorSpaceCount = static_cast<int>(m_outputColorSpaceCstrNames.size()) - 1;
  161. if(m_outputColorSpaceIndex < 0 || m_outputColorSpaceIndex >= outputColorSpaceCount)
  162. {
  163. std::ostringstream err;
  164. err << "Output color space index (" << m_outputColorSpaceIndex << ") out of range.";
  165. error(err.str().c_str());
  166. return;
  167. }
  168. try
  169. {
  170. OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  171. const char * inputName = config->getColorSpaceNameByIndex(m_inputColorSpaceIndex);
  172. const char * outputName = config->getColorSpaceNameByIndex(m_outputColorSpaceIndex);
  173. OCIO::ConstContextRcPtr context = getLocalContext();
  174. m_processor = config->getProcessor(context, inputName, outputName);
  175. }
  176. catch(OCIO::Exception &e)
  177. {
  178. error(e.what());
  179. return;
  180. }
  181. if(m_processor->isNoOp())
  182. {
  183. set_out_channels(DD::Image::Mask_None); // prevents engine() from being called
  184. } else {
  185. set_out_channels(DD::Image::Mask_All);
  186. }
  187. DD::Image::PixelIop::_validate(for_real);
  188. }
  189. // Note that this is copied by others (OCIODisplay)
  190. void OCIOColorSpace::in_channels(int /* n unused */, DD::Image::ChannelSet& mask) const
  191. {
  192. DD::Image::ChannelSet done;
  193. foreach(c, mask)
  194. {
  195. if (DD::Image::colourIndex(c) < 3 && !(done & c))
  196. {
  197. done.addBrothers(c, 3);
  198. }
  199. }
  200. mask += done;
  201. }
  202. // See Saturation::pixel_engine for a well-commented example.
  203. // Note that this is copied by others (OCIODisplay)
  204. void OCIOColorSpace::pixel_engine(
  205. const DD::Image::Row& in,
  206. int /* rowY unused */, int rowX, int rowXBound,
  207. DD::Image::ChannelMask outputChannels,
  208. DD::Image::Row& out)
  209. {
  210. int rowWidth = rowXBound - rowX;
  211. DD::Image::ChannelSet done;
  212. foreach (requestedChannel, outputChannels)
  213. {
  214. // Skip channels which had their trios processed already,
  215. if (done & requestedChannel)
  216. {
  217. continue;
  218. }
  219. // Pass through channels which are not selected for processing
  220. // and non-rgb channels.
  221. if (colourIndex(requestedChannel) >= 3)
  222. {
  223. out.copy(in, requestedChannel, rowX, rowXBound);
  224. continue;
  225. }
  226. DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0);
  227. DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1);
  228. DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2);
  229. done += rChannel;
  230. done += gChannel;
  231. done += bChannel;
  232. const float *rIn = in[rChannel] + rowX;
  233. const float *gIn = in[gChannel] + rowX;
  234. const float *bIn = in[bChannel] + rowX;
  235. float *rOut = out.writable(rChannel) + rowX;
  236. float *gOut = out.writable(gChannel) + rowX;
  237. float *bOut = out.writable(bChannel) + rowX;
  238. // OCIO modifies in-place
  239. // Note: xOut can equal xIn in some circumstances, such as when the
  240. // 'Black' (throwaway) scanline is uses. We thus must guard memcpy,
  241. // which does not allow for overlapping regions.
  242. if (rOut != rIn) memcpy(rOut, rIn, sizeof(float)*rowWidth);
  243. if (gOut != gIn) memcpy(gOut, gIn, sizeof(float)*rowWidth);
  244. if (bOut != bIn) memcpy(bOut, bIn, sizeof(float)*rowWidth);
  245. try
  246. {
  247. OCIO::PlanarImageDesc img(rOut, gOut, bOut, NULL, rowWidth, /*height*/ 1);
  248. m_processor->apply(img);
  249. }
  250. catch(OCIO::Exception &e)
  251. {
  252. error(e.what());
  253. }
  254. }
  255. }
  256. const DD::Image::Op::Description OCIOColorSpace::description("OCIOColorSpace", build);
  257. const char* OCIOColorSpace::Class() const
  258. {
  259. return description.name;
  260. }
  261. const char* OCIOColorSpace::displayName() const
  262. {
  263. return description.name;
  264. }
  265. const char* OCIOColorSpace::node_help() const
  266. {
  267. // TODO more detailed help text
  268. return "Use OpenColorIO to convert from one color space to another.";
  269. }
  270. // This class is necessary in order to call knobsAtTheEnd(). Otherwise, the NukeWrapper knobs
  271. // will be added to the Context tab instead of the primary tab.
  272. class OCIOColorSpaceNukeWrapper : public DD::Image::NukeWrapper
  273. {
  274. public:
  275. OCIOColorSpaceNukeWrapper(DD::Image::PixelIop* op) : DD::Image::NukeWrapper(op)
  276. {
  277. }
  278. virtual void attach()
  279. {
  280. wrapped_iop()->attach();
  281. }
  282. virtual void detach()
  283. {
  284. wrapped_iop()->detach();
  285. }
  286. virtual void knobs(DD::Image::Knob_Callback f)
  287. {
  288. OCIOColorSpace* csIop = dynamic_cast<OCIOColorSpace*>(wrapped_iop());
  289. if(!csIop) return;
  290. DD::Image::NukeWrapper::knobs(f);
  291. DD::Image::Tab_knob(f, "Context");
  292. {
  293. DD::Image::String_knob(f, &csIop->m_contextKey1, "key1");
  294. DD::Image::Spacer(f, 10);
  295. DD::Image::String_knob(f, &csIop->m_contextValue1, "value1");
  296. DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE);
  297. DD::Image::String_knob(f, &csIop->m_contextKey2, "key2");
  298. DD::Image::Spacer(f, 10);
  299. DD::Image::String_knob(f, &csIop->m_contextValue2, "value2");
  300. DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE);
  301. DD::Image::String_knob(f, &csIop->m_contextKey3, "key3");
  302. DD::Image::Spacer(f, 10);
  303. DD::Image::String_knob(f, &csIop->m_contextValue3, "value3");
  304. DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE);
  305. DD::Image::String_knob(f, &csIop->m_contextKey4, "key4");
  306. DD::Image::Spacer(f, 10);
  307. DD::Image::String_knob(f, &csIop->m_contextValue4, "value4");
  308. DD::Image::ClearFlags(f, DD::Image::Knob::STARTLINE);
  309. }
  310. }
  311. };
  312. static DD::Image::Op* build(Node *node)
  313. {
  314. DD::Image::NukeWrapper *op = (new OCIOColorSpaceNukeWrapper(new OCIOColorSpace(node)));
  315. op->channels(DD::Image::Mask_RGB);
  316. return op;
  317. }