/src/nuke/OCIOFileTransform/OCIOFileTransform.cpp

http://github.com/imageworks/OpenColorIO · C++ · 287 lines · 210 code · 51 blank · 26 comment · 32 complexity · 51c569244c50e2e30fea1df5e12f554b MD5 · raw file

  1. /**
  2. * OpenColorIO FileTransform Iop.
  3. */
  4. #include "OCIOFileTransform.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. OCIOFileTransform::OCIOFileTransform(Node *n) : DD::Image::PixelIop(n)
  15. {
  16. m_file = NULL;
  17. m_dirindex = 0;
  18. m_interpindex = 1;
  19. m_reload_version = 1;
  20. }
  21. OCIOFileTransform::~OCIOFileTransform()
  22. {
  23. }
  24. const char* OCIOFileTransform::dirs[] = { "forward", "inverse", 0 };
  25. const char* OCIOFileTransform::interp[] = { "nearest", "linear", "tetrahedral", "best", 0 };
  26. void OCIOFileTransform::knobs(DD::Image::Knob_Callback f)
  27. {
  28. File_knob(f, &m_file, "file", "file");
  29. DD::Image::Tooltip(f, "Specify the file, on disk, to use for this transform. See the node help for the list of supported formats.");
  30. // Reload button, and hidden "version" knob to invalidate cache on reload
  31. Button(f, "reload", "reload");
  32. DD::Image::Tooltip(f, "Reloads specified files");
  33. Int_knob(f, &m_reload_version, "version");
  34. DD::Image::SetFlags(f, DD::Image::Knob::HIDDEN);
  35. String_knob(f, &m_cccid, "cccid");
  36. const char * srchelp2 = "If the source file is an ASC CDL CCC (color correction collection), "
  37. "this specifys the id to lookup. OpenColorIO::Contexts (envvars) are obeyed.";
  38. DD::Image::Tooltip(f, srchelp2);
  39. DD::Image::PyScript_knob(f, "import ocionuke.cdl; ocionuke.cdl.select_cccid_for_filetransform(fileknob='file', cccidknob = 'cccid')", "select_cccid", "select cccid");
  40. Enumeration_knob(f, &m_dirindex, dirs, "direction", "direction");
  41. DD::Image::Tooltip(f, "Specify the transform direction.");
  42. Enumeration_knob(f, &m_interpindex, interp, "interpolation", "interpolation");
  43. DD::Image::Tooltip(f, "Specify the interpolation method. For files that are not LUTs (mtx, etc) this is ignored.");
  44. }
  45. void OCIOFileTransform::_validate(bool for_real)
  46. {
  47. if(!m_file)
  48. {
  49. error("The source file must be specified.");
  50. return;
  51. }
  52. try
  53. {
  54. OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
  55. OCIO::FileTransformRcPtr transform = OCIO::FileTransform::Create();
  56. transform->setSrc(m_file);
  57. transform->setCCCId(m_cccid.c_str());
  58. if(m_dirindex == 0) transform->setDirection(OCIO::TRANSFORM_DIR_FORWARD);
  59. else transform->setDirection(OCIO::TRANSFORM_DIR_INVERSE);
  60. if(m_interpindex == 0) transform->setInterpolation(OCIO::INTERP_NEAREST);
  61. else if(m_interpindex == 1) transform->setInterpolation(OCIO::INTERP_LINEAR);
  62. else if(m_interpindex == 2) transform->setInterpolation(OCIO::INTERP_TETRAHEDRAL);
  63. else if(m_interpindex == 3) transform->setInterpolation(OCIO::INTERP_BEST);
  64. else
  65. {
  66. // Should never happen
  67. error("Interpolation value out of bounds");
  68. return;
  69. }
  70. m_processor = config->getProcessor(transform, OCIO::TRANSFORM_DIR_FORWARD);
  71. }
  72. catch(OCIO::Exception &e)
  73. {
  74. error(e.what());
  75. return;
  76. }
  77. if(m_processor->isNoOp())
  78. {
  79. set_out_channels(DD::Image::Mask_None); // prevents engine() from being called
  80. } else {
  81. set_out_channels(DD::Image::Mask_All);
  82. }
  83. DD::Image::PixelIop::_validate(for_real);
  84. }
  85. // Note that this is copied by others (OCIODisplay)
  86. void OCIOFileTransform::in_channels(int /* n unused */, DD::Image::ChannelSet& mask) const
  87. {
  88. DD::Image::ChannelSet done;
  89. foreach(c, mask)
  90. {
  91. if (DD::Image::colourIndex(c) < 3 && !(done & c))
  92. {
  93. done.addBrothers(c, 3);
  94. }
  95. }
  96. mask += done;
  97. }
  98. void OCIOFileTransform::append(DD::Image::Hash& nodehash)
  99. {
  100. // There is a bug where in Nuke <6.3 the String_knob (used for
  101. // cccid) is not included in the node's hash. Include it manually
  102. // so the node correctly redraws. Appears fixed in in 6.3
  103. nodehash.append(m_cccid.c_str());
  104. // Incremented to force reloading after rereading the LUT file
  105. nodehash.append(m_reload_version);
  106. }
  107. int OCIOFileTransform::knob_changed(DD::Image::Knob* k)
  108. {
  109. // Only show the cccid knob when loading a .cc/.ccc file. Set
  110. // hidden state when the src is changed, or the node properties
  111. // are shown
  112. if(k->is("file") | k->is("showPanel"))
  113. {
  114. // Convoluted equiv to pysting::endswith(m_file, ".ccc")
  115. // TODO: Could this be queried from the processor?
  116. const std::string srcstring = m_file;
  117. const std::string cccext = "ccc";
  118. const std::string ccext = "cc";
  119. if(std::equal(cccext.rbegin(), cccext.rend(), srcstring.rbegin()) ||
  120. std::equal(ccext.rbegin(), ccext.rend(), srcstring.rbegin()))
  121. {
  122. knob("cccid")->show();
  123. knob("select_cccid")->show();
  124. }
  125. else
  126. {
  127. knob("cccid")->hide();
  128. knob("select_cccid")->hide();
  129. }
  130. // Ensure this callback is always triggered (for src knob)
  131. return true;
  132. }
  133. if(k->is("reload"))
  134. {
  135. knob("version")->set_value(m_reload_version+1);
  136. OCIO::ClearAllCaches();
  137. return true; // ensure callback is triggered again
  138. }
  139. // Return zero to avoid callbacks for other knobs
  140. return false;
  141. }
  142. // See Saturation::pixel_engine for a well-commented example.
  143. // Note that this is copied by others (OCIODisplay)
  144. void OCIOFileTransform::pixel_engine(
  145. const DD::Image::Row& in,
  146. int /* rowY unused */, int rowX, int rowXBound,
  147. DD::Image::ChannelMask outputChannels,
  148. DD::Image::Row& out)
  149. {
  150. int rowWidth = rowXBound - rowX;
  151. DD::Image::ChannelSet done;
  152. foreach (requestedChannel, outputChannels)
  153. {
  154. // Skip channels which had their trios processed already,
  155. if (done & requestedChannel)
  156. {
  157. continue;
  158. }
  159. // Pass through channels which are not selected for processing
  160. // and non-rgb channels.
  161. if (colourIndex(requestedChannel) >= 3)
  162. {
  163. out.copy(in, requestedChannel, rowX, rowXBound);
  164. continue;
  165. }
  166. DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0);
  167. DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1);
  168. DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2);
  169. done += rChannel;
  170. done += gChannel;
  171. done += bChannel;
  172. const float *rIn = in[rChannel] + rowX;
  173. const float *gIn = in[gChannel] + rowX;
  174. const float *bIn = in[bChannel] + rowX;
  175. float *rOut = out.writable(rChannel) + rowX;
  176. float *gOut = out.writable(gChannel) + rowX;
  177. float *bOut = out.writable(bChannel) + rowX;
  178. // OCIO modifies in-place
  179. // Note: xOut can equal xIn in some circumstances, such as when the
  180. // 'Black' (throwaway) scanline is uses. We thus must guard memcpy,
  181. // which does not allow for overlapping regions.
  182. if (rOut != rIn) memcpy(rOut, rIn, sizeof(float)*rowWidth);
  183. if (gOut != gIn) memcpy(gOut, gIn, sizeof(float)*rowWidth);
  184. if (bOut != bIn) memcpy(bOut, bIn, sizeof(float)*rowWidth);
  185. try
  186. {
  187. OCIO::PlanarImageDesc img(rOut, gOut, bOut, NULL, rowWidth, /*height*/ 1);
  188. m_processor->apply(img);
  189. }
  190. catch(OCIO::Exception &e)
  191. {
  192. error(e.what());
  193. }
  194. }
  195. }
  196. const DD::Image::Op::Description OCIOFileTransform::description("OCIOFileTransform", build);
  197. const char* OCIOFileTransform::Class() const
  198. {
  199. return description.name;
  200. }
  201. const char* OCIOFileTransform::displayName() const
  202. {
  203. return description.name;
  204. }
  205. const char* OCIOFileTransform::node_help() const
  206. {
  207. if(m_nodehelp.empty())
  208. {
  209. const char * helptext =
  210. "Use OpenColorIO to apply a transform loaded from the given "
  211. "file.\n\n"
  212. "This is usually a 1D or 3D LUT file, but can be other file-based "
  213. "transform, for example an ASC ColorCorrection XML file.\n\n"
  214. "Note that the file's transform is applied with no special "
  215. "input/output colorspace handling - so if the file expects "
  216. "log-encoded pixels, but you apply the node to a linear "
  217. "image, you will get incorrect results.\n\n";
  218. std::ostringstream os;
  219. os << helptext;
  220. os << "Supported formats:\n";
  221. for(int i=0; i<OCIO::FileTransform::getNumFormats(); ++i)
  222. {
  223. const char* name = OCIO::FileTransform::getFormatNameByIndex(i);
  224. const char* exten = OCIO::FileTransform::getFormatExtensionByIndex(i);
  225. os << "\n." << exten << " (" << name << ")";
  226. }
  227. m_nodehelp = os.str();
  228. }
  229. return m_nodehelp.c_str();
  230. }
  231. DD::Image::Op* build(Node *node)
  232. {
  233. DD::Image::NukeWrapper *op = new DD::Image::NukeWrapper(new OCIOFileTransform(node));
  234. op->channels(DD::Image::Mask_RGB);
  235. return op;
  236. }