PageRenderTime 44ms CodeModel.GetById 14ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/core/ExponentOps.cpp

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