1 /**
2 * OpenColorIO LogConvert Iop.
3 */
4
5 #include "OCIOLogConvert.h"
6
7 namespace 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
19 const char* OCIOLogConvert::modes[] = {
20 "log to lin", "lin to log", 0
21 };
22
OCIOLogConvert(Node * n)23 OCIOLogConvert::OCIOLogConvert(Node *n) : DD::Image::PixelIop(n)
24 {
25 modeindex = 0;
26 }
27
~OCIOLogConvert()28 OCIOLogConvert::~OCIOLogConvert()
29 {
30
31 }
32
knobs(DD::Image::Knob_Callback f)33 void OCIOLogConvert::knobs(DD::Image::Knob_Callback f)
34 {
35
36 Enumeration_knob(f, &modeindex, modes, "operation", "operation");
37 DD::Image::SetFlags(f, DD::Image::Knob::ALWAYS_SAVE);
38 }
39
_validate(bool for_real)40 void OCIOLogConvert::_validate(bool for_real)
41 {
42 try
43 {
44 OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
45
46 const char * src = 0;
47 const char * dst = 0;
48
49 if(modeindex == 0)
50 {
51 src = OCIO::ROLE_COMPOSITING_LOG;
52 dst = OCIO::ROLE_SCENE_LINEAR;
53 }
54 else
55 {
56 src = OCIO::ROLE_SCENE_LINEAR;
57 dst = OCIO::ROLE_COMPOSITING_LOG;
58 }
59
60 processor = config->getProcessor(src, dst);
61 }
62 catch(OCIO::Exception &e)
63 {
64 error(e.what());
65 return;
66 }
67
68 if(processor->isNoOp())
69 {
70 set_out_channels(DD::Image::Mask_None); // prevents engine() from being called
71 } else {
72 set_out_channels(DD::Image::Mask_All);
73 }
74
75 DD::Image::PixelIop::_validate(for_real);
76 }
77
78 // Note that this is copied by others (OCIODisplay)
in_channels(int,DD::Image::ChannelSet & mask) const79 void OCIOLogConvert::in_channels(int /* n unused */, DD::Image::ChannelSet& mask) const
80 {
81 DD::Image::ChannelSet done;
82 foreach(c, mask)
83 {
84 if (DD::Image::colourIndex(c) < 3 && !(done & c))
85 {
86 done.addBrothers(c, 3);
87 }
88 }
89 mask += done;
90 }
91
92 // See Saturation::pixel_engine for a well-commented example.
93 // Note that this is copied by others (OCIODisplay)
pixel_engine(const DD::Image::Row & in,int,int rowX,int rowXBound,DD::Image::ChannelMask outputChannels,DD::Image::Row & out)94 void OCIOLogConvert::pixel_engine(
95 const DD::Image::Row& in,
96 int /* rowY unused */, int rowX, int rowXBound,
97 DD::Image::ChannelMask outputChannels,
98 DD::Image::Row& out)
99 {
100 int rowWidth = rowXBound - rowX;
101
102 DD::Image::ChannelSet done;
103 foreach (requestedChannel, outputChannels)
104 {
105 // Skip channels which had their trios processed already,
106 if (done & requestedChannel)
107 {
108 continue;
109 }
110
111 // Pass through channels which are not selected for processing
112 // and non-rgb channels.
113 if (colourIndex(requestedChannel) >= 3)
114 {
115 out.copy(in, requestedChannel, rowX, rowXBound);
116 continue;
117 }
118
119 DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0);
120 DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1);
121 DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2);
122
123 done += rChannel;
124 done += gChannel;
125 done += bChannel;
126
127 const float *rIn = in[rChannel] + rowX;
128 const float *gIn = in[gChannel] + rowX;
129 const float *bIn = in[bChannel] + rowX;
130
131 float *rOut = out.writable(rChannel) + rowX;
132 float *gOut = out.writable(gChannel) + rowX;
133 float *bOut = out.writable(bChannel) + rowX;
134
135 // OCIO modifies in-place
136 // Note: xOut can equal xIn in some circumstances, such as when the
137 // 'Black' (throwaway) scanline is uses. We thus must guard memcpy,
138 // which does not allow for overlapping regions.
139 if (rOut != rIn) memcpy(rOut, rIn, sizeof(float)*rowWidth);
140 if (gOut != gIn) memcpy(gOut, gIn, sizeof(float)*rowWidth);
141 if (bOut != bIn) memcpy(bOut, bIn, sizeof(float)*rowWidth);
142
143 try
144 {
145 OCIO::PlanarImageDesc img(rOut, gOut, bOut, NULL, rowWidth, /*height*/ 1);
146 processor->apply(img);
147 }
148 catch(OCIO::Exception &e)
149 {
150 error(e.what());
151 }
152 }
153 }
154
155 const DD::Image::Op::Description OCIOLogConvert::description("OCIOLogConvert", build);
156
Class() const157 const char* OCIOLogConvert::Class() const
158 {
159 return description.name;
160 }
161
displayName() const162 const char* OCIOLogConvert::displayName() const
163 {
164 return description.name;
165 }
166
node_help() const167 const char* OCIOLogConvert::node_help() const
168 {
169 // TODO more detailed help text
170 return "Use OpenColorIO to convert from SCENE_LINEAR to COMPOSITING_LOG (or back).";
171 }
172
173
build(Node * node)174 DD::Image::Op* build(Node *node)
175 {
176 DD::Image::NukeWrapper *op = new DD::Image::NukeWrapper(new OCIOLogConvert(node));
177 op->channels(DD::Image::Mask_RGB);
178 return op;
179 }
180