PageRenderTime 47ms CodeModel.GetById 23ms app.highlight 20ms RepoModel.GetById 1ms app.codeStats 0ms

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