/src/core/LookTransform.cpp
C++ | 405 lines | 306 code | 60 blank | 39 comment | 38 complexity | 7b816b11a8effe7358c5f2f7c429983b MD5 | raw file
Possible License(s): BSD-3-Clause
1/*
2Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
3All Rights Reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8* Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10* Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13* Neither the name of Sony Pictures Imageworks nor the names of its
14 contributors may be used to endorse or promote products derived from
15 this software without specific prior written permission.
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <OpenColorIO/OpenColorIO.h>
30
31#include <algorithm>
32#include <iterator>
33
34#include "LookParse.h"
35#include "NoOps.h"
36#include "OpBuilders.h"
37#include "ParseUtils.h"
38#include "pystring/pystring.h"
39
40
41OCIO_NAMESPACE_ENTER
42{
43 LookTransformRcPtr LookTransform::Create()
44 {
45 return LookTransformRcPtr(new LookTransform(), &deleter);
46 }
47
48 void LookTransform::deleter(LookTransform* t)
49 {
50 delete t;
51 }
52
53
54 class LookTransform::Impl
55 {
56 public:
57 TransformDirection dir_;
58 std::string src_;
59 std::string dst_;
60 std::string looks_;
61
62 Impl() :
63 dir_(TRANSFORM_DIR_FORWARD)
64 { }
65
66 ~Impl()
67 { }
68
69 Impl& operator= (const Impl & rhs)
70 {
71 dir_ = rhs.dir_;
72 src_ = rhs.src_;
73 dst_ = rhs.dst_;
74 looks_ = rhs.looks_;
75 return *this;
76 }
77 };
78
79 ///////////////////////////////////////////////////////////////////////////
80
81
82
83 LookTransform::LookTransform()
84 : m_impl(new LookTransform::Impl)
85 {
86 }
87
88 TransformRcPtr LookTransform::createEditableCopy() const
89 {
90 LookTransformRcPtr transform = LookTransform::Create();
91 *(transform->m_impl) = *m_impl;
92 return transform;
93 }
94
95 LookTransform::~LookTransform()
96 {
97 delete m_impl;
98 m_impl = NULL;
99 }
100
101 LookTransform& LookTransform::operator= (const LookTransform & rhs)
102 {
103 *m_impl = *rhs.m_impl;
104 return *this;
105 }
106
107 TransformDirection LookTransform::getDirection() const
108 {
109 return getImpl()->dir_;
110 }
111
112 void LookTransform::setDirection(TransformDirection dir)
113 {
114 getImpl()->dir_ = dir;
115 }
116
117 const char * LookTransform::getSrc() const
118 {
119 return getImpl()->src_.c_str();
120 }
121
122 void LookTransform::setSrc(const char * src)
123 {
124 getImpl()->src_ = src;
125 }
126
127 const char * LookTransform::getDst() const
128 {
129 return getImpl()->dst_.c_str();
130 }
131
132 void LookTransform::setDst(const char * dst)
133 {
134 getImpl()->dst_ = dst;
135 }
136
137 void LookTransform::setLooks(const char * looks)
138 {
139 getImpl()->looks_ = looks;
140 }
141
142 const char * LookTransform::getLooks() const
143 {
144 return getImpl()->looks_.c_str();
145 }
146
147 std::ostream& operator<< (std::ostream& os, const LookTransform& t)
148 {
149 os << "<LookTransform ";
150 os << "src=" << t.getSrc() << ", ";
151 os << "dst=" << t.getDst() << ", ";
152 os << "looks=" << t.getLooks() << ", ";
153 os << "direction=" << TransformDirectionToString(t.getDirection()) << ", ";
154 os << ">\n";
155 return os;
156 }
157
158 ////////////////////////////////////////////////////////////////////////////
159
160
161
162
163 namespace
164 {
165
166 void RunLookTokens(OpRcPtrVec & ops,
167 ConstColorSpaceRcPtr & currentColorSpace,
168 bool skipColorSpaceConversions,
169 const Config& config,
170 const ConstContextRcPtr & context,
171 const LookParseResult::Tokens & lookTokens)
172 {
173 if(lookTokens.empty()) return;
174
175 for(unsigned int i=0; i<lookTokens.size(); ++i)
176 {
177 const std::string & lookName = lookTokens[i].name;
178
179 if(lookName.empty()) continue;
180
181 ConstLookRcPtr look = config.getLook(lookName.c_str());
182 if(!look)
183 {
184 std::ostringstream os;
185 os << "RunLookTokens error. ";
186 os << "The specified look, '" << lookName;
187 os << "', cannot be found. ";
188 if(config.getNumLooks() == 0)
189 {
190 os << " (No looks defined in config)";
191 }
192 else
193 {
194 os << " (looks: ";
195 for(int ii=0; ii<config.getNumLooks(); ++ii)
196 {
197 if(ii != 0) os << ", ";
198 os << config.getLookNameByIndex(ii);
199 }
200 os << ")";
201 }
202
203 throw Exception(os.str().c_str());
204 }
205
206 // Put the new ops into a temp array, to see if it's a no-op
207 // If it is a no-op, dont bother doing the colorspace conversion.
208 OpRcPtrVec tmpOps;
209
210 if(lookTokens[i].dir == TRANSFORM_DIR_FORWARD)
211 {
212 CreateLookNoOp(tmpOps, lookName);
213 if(look->getTransform())
214 {
215 BuildOps(tmpOps, config, context, look->getTransform(), TRANSFORM_DIR_FORWARD);
216 }
217 else if(look->getInverseTransform())
218 {
219 BuildOps(tmpOps, config, context, look->getInverseTransform(), TRANSFORM_DIR_INVERSE);
220 }
221 }
222 else if(lookTokens[i].dir == TRANSFORM_DIR_INVERSE)
223 {
224 CreateLookNoOp(tmpOps, std::string("-") + lookName);
225 if(look->getInverseTransform())
226 {
227 BuildOps(tmpOps, config, context, look->getInverseTransform(), TRANSFORM_DIR_FORWARD);
228 }
229 else if(look->getTransform())
230 {
231 BuildOps(tmpOps, config, context, look->getTransform(), TRANSFORM_DIR_INVERSE);
232 }
233 }
234 else
235 {
236 std::ostringstream os;
237 os << "BuildLookOps error. ";
238 os << "The specified look, '" << lookTokens[i].name;
239 os << "' has an ill-defined transform direction.";
240 throw Exception(os.str().c_str());
241 }
242
243 if(!IsOpVecNoOp(tmpOps))
244 {
245 if(!skipColorSpaceConversions)
246 {
247 ConstColorSpaceRcPtr processColorSpace = config.getColorSpace(look->getProcessSpace());
248 if(!processColorSpace)
249 {
250 std::ostringstream os;
251 os << "RunLookTokens error. ";
252 os << "The specified look, '" << lookTokens[i].name;
253 os << "', requires processing in the ColorSpace, '";
254 os << look->getProcessSpace() << "' which is not defined.";
255 throw Exception(os.str().c_str());
256 }
257
258 BuildColorSpaceOps(ops, config, context,
259 currentColorSpace,
260 processColorSpace);
261 currentColorSpace = processColorSpace;
262 }
263
264 std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
265 }
266 }
267 }
268
269 } // anon namespace
270
271 ////////////////////////////////////////////////////////////////////////////
272
273
274 void BuildLookOps(OpRcPtrVec & ops,
275 const Config& config,
276 const ConstContextRcPtr & context,
277 const LookTransform & lookTransform,
278 TransformDirection dir)
279 {
280 ConstColorSpaceRcPtr src, dst;
281 src = config.getColorSpace( lookTransform.getSrc() );
282 dst = config.getColorSpace( lookTransform.getDst() );
283
284 if(!src)
285 {
286 std::ostringstream os;
287 os << "BuildLookOps error.";
288 os << "The specified lookTransform specifies a src colorspace, '";
289 os << lookTransform.getSrc() << "', which is not defined.";
290 throw Exception(os.str().c_str());
291 }
292
293 if(!dst)
294 {
295 std::ostringstream os;
296 os << "BuildLookOps error.";
297 os << "The specified lookTransform specifies a dst colorspace, '";
298 os << lookTransform.getDst() << "', which is not defined.";
299 throw Exception(os.str().c_str());
300 }
301
302 LookParseResult looks;
303 looks.parse(lookTransform.getLooks());
304
305 // We must handle the inverse src/dst colorspace transformation explicitly.
306 if(dir == TRANSFORM_DIR_INVERSE)
307 {
308 std::swap(src, dst);
309 looks.reverse();
310 }
311 else if(dir == TRANSFORM_DIR_UNKNOWN)
312 {
313 std::ostringstream os;
314 os << "BuildLookOps error. A valid transform direction must be specified.";
315 throw Exception(os.str().c_str());
316 }
317
318 ConstColorSpaceRcPtr currentColorSpace = src;
319 BuildLookOps(ops,
320 currentColorSpace,
321 false,
322 config,
323 context,
324 looks);
325
326 BuildColorSpaceOps(ops, config, context,
327 currentColorSpace,
328 dst);
329 }
330
331 void BuildLookOps(OpRcPtrVec & ops,
332 ConstColorSpaceRcPtr & currentColorSpace,
333 bool skipColorSpaceConversions,
334 const Config& config,
335 const ConstContextRcPtr & context,
336 const LookParseResult & looks)
337 {
338 const LookParseResult::Options & options = looks.getOptions();
339
340 if(options.empty())
341 {
342 // Do nothing
343 }
344 else if(options.size() == 1)
345 {
346 // As an optimization, if we only have a single look option,
347 // just push back onto the final location
348 RunLookTokens(ops,
349 currentColorSpace,
350 skipColorSpaceConversions,
351 config,
352 context,
353 options[0]);
354 }
355 else
356 {
357 // If we have multiple look options, try each one in order,
358 // and if we can create the ops without a missing file exception,
359 // push back it's results and return
360
361 bool success = false;
362 std::ostringstream os;
363
364 OpRcPtrVec tmpOps;
365 ConstColorSpaceRcPtr cs;
366
367 for(unsigned int i=0; i<options.size(); ++i)
368 {
369 cs = currentColorSpace;
370 tmpOps.clear();
371
372 try
373 {
374 RunLookTokens(tmpOps,
375 cs,
376 skipColorSpaceConversions,
377 config,
378 context,
379 options[i]);
380 success = true;
381 break;
382 }
383 catch(ExceptionMissingFile & e)
384 {
385 if(i != 0) os << " ... ";
386
387 os << "(";
388 LookParseResult::serialize(os, options[i]);
389 os << ") " << e.what();
390 }
391 }
392
393 if(success)
394 {
395 currentColorSpace = cs;
396 std::copy(tmpOps.begin(), tmpOps.end(), std::back_inserter(ops));
397 }
398 else
399 {
400 throw ExceptionMissingFile(os.str().c_str());
401 }
402 }
403 }
404}
405OCIO_NAMESPACE_EXIT