1 /*
2 Copyright (c) 2003-2010 Sony Pictures Imageworks Inc., et al.
3 All Rights Reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
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.
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <cmath>
30 #include <cstring>
31 #include <sstream>
32 #include <algorithm>
33
34 #include <OpenColorIO/OpenColorIO.h>
35
36 #include "ExponentOps.h"
37 #include "GpuShaderUtils.h"
38 #include "MathUtils.h"
39
40 OCIO_NAMESPACE_ENTER
41 {
42 namespace
43 {
44 void ApplyClampExponent(float* rgbaBuffer, long numPixels,
45 const float* exp4)
46 {
47 for(long pixelIndex=0; pixelIndex<numPixels; ++pixelIndex)
48 {
49 rgbaBuffer[0] = powf( std::max(0.0f, rgbaBuffer[0]), exp4[0]);
50 rgbaBuffer[1] = powf( std::max(0.0f, rgbaBuffer[1]), exp4[1]);
51 rgbaBuffer[2] = powf( std::max(0.0f, rgbaBuffer[2]), exp4[2]);
52 rgbaBuffer[3] = powf( std::max(0.0f, rgbaBuffer[3]), exp4[3]);
53 rgbaBuffer += 4;
54 }
55 }
56
57 const int FLOAT_DECIMALS = 7;
58 }
59
60
61 namespace
62 {
63 class ExponentOp : public Op
64 {
65 public:
66 ExponentOp(const double * exp4,
67 TransformDirection direction);
68 virtual ~ExponentOp();
69
70 virtual OpRcPtr clone() const;
71
72 virtual std::string getInfo() const;
73 virtual std::string getCacheID() const;
74
75 virtual bool isNoOp() const;
76 virtual bool isSameType(const OpRcPtr & op) const;
77 virtual bool isInverse(const OpRcPtr & op) const;
78
79 virtual bool canCombineWith(const OpRcPtr & op) const;
80 virtual void combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const;
81
82 virtual bool hasChannelCrosstalk() const;
83 virtual void finalize();
84 virtual void apply(float* rgbaBuffer, long numPixels) const;
85
86 virtual bool supportsGpuShader() const;
87 virtual void writeGpuShader(std::ostream & shader,
88 const std::string & pixelName,
89 const GpuShaderDesc & shaderDesc) const;
90 private:
91 double m_exp4[4];
92
93 // Set in finalize
94 std::string m_cacheID;
95 };
96
97 typedef OCIO_SHARED_PTR<ExponentOp> ExponentOpRcPtr;
98
99
100 ExponentOp::ExponentOp(const double * exp4,
101 TransformDirection direction):
102 Op()
103 {
104 if(direction == TRANSFORM_DIR_UNKNOWN)
105 {
106 throw Exception("Cannot create ExponentOp with unspecified transform direction.");
107 }
108
109 if(direction == TRANSFORM_DIR_INVERSE)
110 {
111 for(int i=0; i<4; ++i)
112 {
113 if(!IsScalarEqualToZeroFlt(exp4[i]))
114 {
115 m_exp4[i] = 1.0 / exp4[i];
116 }
117 else
118 {
119 throw Exception("Cannot apply ExponentOp op, Cannot apply 0.0 exponent in the inverse.");
120 }
121 }
122 }
123 else
124 {
125 memcpy(m_exp4, exp4, 4*sizeof(double));
126 }
127 }
128
129 OpRcPtr ExponentOp::clone() const
130 {
131 OpRcPtr op = OpRcPtr(new ExponentOp(m_exp4, TRANSFORM_DIR_FORWARD));
132 return op;
133 }
134
135 ExponentOp::~ExponentOp()
136 { }
137
138 std::string ExponentOp::getInfo() const
139 {
140 return "<ExponentOp>";
141 }
142
143 std::string ExponentOp::getCacheID() const
144 {
145 return m_cacheID;
146 }
147
148 bool ExponentOp::isNoOp() const
149 {
150 return IsVecEqualToOneFlt(m_exp4, 4);
151 }
152
153 bool ExponentOp::isSameType(const OpRcPtr & op) const
154 {
155 ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(op);
156 if(!typedRcPtr) return false;
157 return true;
158 }
159
160 bool ExponentOp::isInverse(const OpRcPtr & op) const
161 {
162 ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(op);
163 if(!typedRcPtr) return false;
164
165 double combined[4] = { m_exp4[0]*typedRcPtr->m_exp4[0],
166 m_exp4[1]*typedRcPtr->m_exp4[1],
167 m_exp4[2]*typedRcPtr->m_exp4[2],
168 m_exp4[3]*typedRcPtr->m_exp4[3] };
169
170 return IsVecEqualToOneFlt(combined, 4);
171 }
172
173 bool ExponentOp::canCombineWith(const OpRcPtr & op) const
174 {
175 return isSameType(op);
176 }
177
178 void ExponentOp::combineWith(OpRcPtrVec & ops, const OpRcPtr & secondOp) const
179 {
180 ExponentOpRcPtr typedRcPtr = DynamicPtrCast<ExponentOp>(secondOp);
181 if(!typedRcPtr)
182 {
183 std::ostringstream os;
184 os << "ExponentOp can only be combined with other ";
185 os << "ExponentOps. secondOp:" << secondOp->getInfo();
186 throw Exception(os.str().c_str());
187 }
188
189 double combined[4] = { m_exp4[0]*typedRcPtr->m_exp4[0],
190 m_exp4[1]*typedRcPtr->m_exp4[1],
191 m_exp4[2]*typedRcPtr->m_exp4[2],
192 m_exp4[3]*typedRcPtr->m_exp4[3] };
193
194 if(!IsVecEqualToOneFlt(combined, 4))
195 {
196 ops.push_back(
197 ExponentOpRcPtr(new ExponentOp(combined,
198 TRANSFORM_DIR_FORWARD)) );
199 }
200 }
201
202 bool ExponentOp::hasChannelCrosstalk() const
203 {
204 return false;
205 }
206
207 void ExponentOp::finalize()
208 {
209 // Create the cacheID
210 std::ostringstream cacheIDStream;
211 cacheIDStream << "<ExponentOp ";
212 cacheIDStream.precision(FLOAT_DECIMALS);
213 for(int i=0; i<4; ++i)
214 {
215 cacheIDStream << m_exp4[i] << " ";
216 }
217 cacheIDStream << ">";
218 m_cacheID = cacheIDStream.str();
219 }
220
221 void ExponentOp::apply(float* rgbaBuffer, long numPixels) const
222 {
223 if(!rgbaBuffer) return;
224 float exp[4] = { float(m_exp4[0]), float(m_exp4[1]),
225 float(m_exp4[2]), float(m_exp4[3]) };
226 ApplyClampExponent(rgbaBuffer, numPixels, exp);
227 }
228
229 bool ExponentOp::supportsGpuShader() const
230 {
231 return true;
232 }
233
234 void ExponentOp::writeGpuShader(std::ostream & shader,
235 const std::string & pixelName,
236 const GpuShaderDesc & shaderDesc) const
237 {
238 float exp[4] = { float(m_exp4[0]), float(m_exp4[1]),
239 float(m_exp4[2]), float(m_exp4[3]) };
240
241 GpuLanguage lang = shaderDesc.getLanguage();
242 float zerovec[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
243
244 shader << pixelName << " = pow(";
245 shader << "max(" << pixelName << ", " << GpuTextHalf4(zerovec, lang) << ")";
246 shader << ", " << GpuTextHalf4(exp, lang) << ");\n";
247 }
248
249 } // Anon namespace
250
251
252
253 void CreateExponentOp(OpRcPtrVec & ops,
254 const float * exp4,
255 TransformDirection direction)
256 {
257 bool expIsIdentity = IsVecEqualToOne(exp4, 4);
258 if(expIsIdentity) return;
259 double d_exp[4] = { double(exp4[0]), double(exp4[1]),
260 double(exp4[2]), double(exp4[3]) };
261 ops.push_back( ExponentOpRcPtr(new ExponentOp(d_exp, direction)) );
262 }
263 }
264 OCIO_NAMESPACE_EXIT
265
266
267 ///////////////////////////////////////////////////////////////////////////////
268
269 #ifdef OCIO_UNIT_TEST
270
271 #include "UnitTest.h"
272
273 OCIO_NAMESPACE_USING
274
275 OIIO_ADD_TEST(ExponentOps, Value)
276 {
277 float exp1[4] = { 1.2f, 1.3f, 1.4f, 1.5f };
278
279 OpRcPtrVec ops;
280 CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
281 CreateExponentOp(ops, exp1, TRANSFORM_DIR_INVERSE);
282 OIIO_CHECK_EQUAL(ops.size(), 2);
283
284 for(unsigned int i=0; i<ops.size(); ++i)
285 {
286 ops[i]->finalize();
287 }
288
289 float error = 1e-6f;
290
291 const float source[] = { 0.5f, 0.5f, 0.5f, 0.5f, };
292
293 const float result1[] = { 0.43527528164806206f, 0.40612619817811774f,
294 0.37892914162759955f, 0.35355339059327379f };
295
296 float tmp[4];
297 memcpy(tmp, source, 4*sizeof(float));
298 ops[0]->apply(tmp, 1);
299
300 for(unsigned int i=0; i<4; ++i)
301 {
302 OIIO_CHECK_CLOSE(tmp[i], result1[i], error);
303 }
304
305 ops[1]->apply(tmp, 1);
306 for(unsigned int i=0; i<4; ++i)
307 {
308 OIIO_CHECK_CLOSE(tmp[i], source[i], error);
309 }
310 }
311
OIIO_ADD_TEST(ExponentOps,Inverse)312 OIIO_ADD_TEST(ExponentOps, Inverse)
313 {
314 float exp1[4] = { 2.0f, 1.02345f, 5.651321f, 0.12345678910f };
315 float exp2[4] = { 2.0f, 2.0f, 2.0f, 2.0f };
316
317 OpRcPtrVec ops;
318
319 CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
320 CreateExponentOp(ops, exp1, TRANSFORM_DIR_INVERSE);
321 CreateExponentOp(ops, exp2, TRANSFORM_DIR_FORWARD);
322 CreateExponentOp(ops, exp2, TRANSFORM_DIR_INVERSE);
323
324 OIIO_CHECK_EQUAL(ops.size(), 4);
325
326 OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[1]));
327 OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[2]));
328 OIIO_CHECK_ASSERT(ops[0]->isSameType(ops[3]->clone()));
329
330 OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[0]), false);
331 OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[1]), true);
332 OIIO_CHECK_EQUAL(ops[1]->isInverse(ops[0]), true);
333 OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[2]), false);
334 OIIO_CHECK_EQUAL(ops[0]->isInverse(ops[3]), false);
335 OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[0]), false);
336 OIIO_CHECK_EQUAL(ops[2]->isInverse(ops[3]), true);
337 OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[2]), true);
338 OIIO_CHECK_EQUAL(ops[3]->isInverse(ops[3]), false);
339 }
340
OIIO_ADD_TEST(ExponentOps,Combining)341 OIIO_ADD_TEST(ExponentOps, Combining)
342 {
343 {
344 float exp1[4] = { 2.0f, 2.0f, 2.0f, 1.0f };
345 float exp2[4] = { 1.2f, 1.2f, 1.2f, 1.0f };
346
347 OpRcPtrVec ops;
348 CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
349 CreateExponentOp(ops, exp2, TRANSFORM_DIR_FORWARD);
350 OIIO_CHECK_EQUAL(ops.size(), 2);
351
352 float error = 1e-6f;
353 const float source[] = { 0.5f, 0.5f, 0.5f, 0.5f, };
354 const float result[] = { 0.18946457081379978f, 0.18946457081379978f,
355 0.18946457081379978f, 0.5f };
356
357 float tmp[4];
358 memcpy(tmp, source, 4*sizeof(float));
359 ops[0]->apply(tmp, 1);
360 ops[1]->apply(tmp, 1);
361
362 for(unsigned int i=0; i<4; ++i)
363 {
364 OIIO_CHECK_CLOSE(tmp[i], result[i], error);
365 }
366
367
368 OpRcPtrVec combined;
369 ops[0]->combineWith(combined, ops[1]);
370 OIIO_CHECK_EQUAL(combined.size(), 1);
371
372 float tmp2[4];
373 memcpy(tmp2, source, 4*sizeof(float));
374 combined[0]->apply(tmp2, 1);
375
376 for(unsigned int i=0; i<4; ++i)
377 {
378 OIIO_CHECK_CLOSE(tmp2[i], result[i], error);
379 }
380 }
381
382 {
383
384 float exp1[4] = {1.037289f, 1.019015f, 0.966082f, 1.0f};
385
386 OpRcPtrVec ops;
387 CreateExponentOp(ops, exp1, TRANSFORM_DIR_FORWARD);
388 CreateExponentOp(ops, exp1, TRANSFORM_DIR_INVERSE);
389 OIIO_CHECK_EQUAL(ops.size(), 2);
390
391 bool isInverse = ops[0]->isInverse(ops[1]);
392 OIIO_CHECK_EQUAL(isInverse, true);
393 }
394 }
395
396 #endif // OCIO_UNIT_TEST
397