[PATCH 02/15] Made burn and dodge CompositeOs compatible with Adobe Photoshop (c)

Silvio Heinrich plassy at web.de
Fri Jan 7 19:14:15 CET 2011


Added a new generic base class for CompositeOps, currently only used by
KoCompositeOpBurn and KoCompositeOpDodge.
---
 libs/pigment/compositeops/KoCompositeOpBurn.h      |   49 ++---
 libs/pigment/compositeops/KoCompositeOpDodge.h     |   49 +++---
 libs/pigment/compositeops/KoCompositeOpFunctions.h |  193 ++++++++++++++++++++
 3 files changed, 236 insertions(+), 55 deletions(-)
 create mode 100644 libs/pigment/compositeops/KoCompositeOpFunctions.h

diff --git a/libs/pigment/compositeops/KoCompositeOpBurn.h b/libs/pigment/compositeops/KoCompositeOpBurn.h
index 2a56e2f..3315373 100644
--- a/libs/pigment/compositeops/KoCompositeOpBurn.h
+++ b/libs/pigment/compositeops/KoCompositeOpBurn.h
@@ -20,51 +20,42 @@
 #ifndef KOCOMPOSITEOPBURN_H_
 #define KOCOMPOSITEOPBURN_H_
 
-#include "KoCompositeOpAlphaBase.h"
+#include "KoCompositeOpFunctions.h"
 
 /**
  * A template version of the burn composite operation to use in colorspaces.
  */
 template<class _CSTraits>
-class KoCompositeOpBurn : public KoCompositeOpAlphaBase<_CSTraits, KoCompositeOpBurn<_CSTraits>, true >
+class KoCompositeOpBurn : public KoCompositeOpBase< _CSTraits, KoCompositeOpBurn<_CSTraits> >
 {
     typedef typename _CSTraits::channels_type channels_type;
     typedef typename KoColorSpaceMathsTraits<channels_type>::compositetype composite_type;
+    static const qint32 channels_nb = _CSTraits::channels_nb;
+    static const qint32 alpha_pos   = _CSTraits::alpha_pos;
     
 public:
-
-    KoCompositeOpBurn(const KoColorSpace * cs)
-            : KoCompositeOpAlphaBase<_CSTraits, KoCompositeOpBurn<_CSTraits>, true >(cs, COMPOSITE_BURN, i18n("Burn"), KoCompositeOp::categoryLight()) {
-    }
+    KoCompositeOpBurn(const KoColorSpace* cs)
+        : KoCompositeOpBase< _CSTraits, KoCompositeOpBurn<_CSTraits> >(cs, COMPOSITE_BURN, i18n("Burn"), KoCompositeOp::categoryLight()) { }
 
 public:
-    inline static channels_type selectAlpha(channels_type srcAlpha, channels_type dstAlpha) {
-        return qMin(srcAlpha, dstAlpha);
-    }
-
-    inline static void composeColorChannels(channels_type srcBlend,
-                                            const channels_type* src,
-                                            channels_type* dst,
-                                            bool allChannelFlags,
-                                            const QBitArray & channelFlags) {
-        for (uint i = 0; i < _CSTraits::channels_nb; i++) {
-            if ((int)i != _CSTraits::alpha_pos && (allChannelFlags || channelFlags.testBit(i))) {
-                composite_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
-                composite_type invDst    = unitValue - dst[i];
-                
-                if(src[i] != KoColorSpaceMathsTraits<channels_type>::zeroValue) {
-                    composite_type result = unitValue - qMin<composite_type>(invDst * unitValue / src[i], unitValue);
-                    dst[i] = KoColorSpaceMaths<channels_type>::blend(result, dst[i], srcBlend);
-                }
-                else {
-                    //composite_type result = KoColorSpaceMathsTraits<channels_type>::zeroValue;
-                    composite_type result = unitValue - qMin<composite_type>(invDst * unitValue, unitValue);
-                    dst[i] = KoColorSpaceMaths<channels_type>::blend(result, dst[i], srcBlend);
+    inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
+                                                     channels_type* dst, channels_type dstAlpha,
+                                                     channels_type opacity, const QBitArray& channelFlags) {
+        srcAlpha = mul(srcAlpha, opacity);
+        
+        channels_type newDstAlpha = unionShapeOpacy(srcAlpha, dstAlpha);
+        
+        if(newDstAlpha != KoColorSpaceMathsTraits<channels_type>::zeroValue) {
+            for(qint32 i=0; i <channels_nb; i++) {
+                if(i != alpha_pos && channelFlags.testBit(i)) {
+                    channels_type result = blend(src[i], srcAlpha, dst[i], dstAlpha, &cfColorBurn<channels_type>);
+                    dst[i] = div(result, newDstAlpha);
                 }
             }
         }
+        
+        return newDstAlpha;
     }
-
 };
 
 #endif
diff --git a/libs/pigment/compositeops/KoCompositeOpDodge.h b/libs/pigment/compositeops/KoCompositeOpDodge.h
index 368a26b..019695a 100644
--- a/libs/pigment/compositeops/KoCompositeOpDodge.h
+++ b/libs/pigment/compositeops/KoCompositeOpDodge.h
@@ -1,6 +1,7 @@
 /*
  *  Copyright (c) 2007 Cyrille Berger <cberger at cberger.net>
- *
+ *  Copyright (c) 2011 Silvio Heinrich <plassy at web.de>
+ * 
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
@@ -20,44 +21,40 @@
 #ifndef _KOCOMPOSITEOPDODGE_H_
 #define _KOCOMPOSITEOPDODGE_H_
 
-#include "KoCompositeOpAlphaBase.h"
-
+#include "KoCompositeOpFunctions.h"
 
 /**
  * A template version of the dodge composite operation to use in colorspaces.
  */
 template<class _CSTraits>
-class KoCompositeOpDodge : public KoCompositeOpAlphaBase<_CSTraits, KoCompositeOpDodge<_CSTraits>, true >
+class KoCompositeOpDodge : public KoCompositeOpBase< _CSTraits, KoCompositeOpDodge<_CSTraits> >
 {
     typedef typename _CSTraits::channels_type channels_type;
-    typedef typename KoColorSpaceMathsTraits<typename _CSTraits::channels_type>::compositetype compositetype;
+    typedef typename KoColorSpaceMathsTraits<channels_type>::compositetype composite_type;
+    static const qint32 channels_nb = _CSTraits::channels_nb;
+    static const qint32 alpha_pos   = _CSTraits::alpha_pos;
 public:
-
     KoCompositeOpDodge(const KoColorSpace * cs)
-            : KoCompositeOpAlphaBase<_CSTraits, KoCompositeOpDodge<_CSTraits>, true >(cs, COMPOSITE_DODGE, i18n("Dodge"), KoCompositeOp::categoryLight()) {
-    }
+        : KoCompositeOpBase< _CSTraits, KoCompositeOpDodge<_CSTraits> >(cs, COMPOSITE_DODGE, i18n("Dodge"), KoCompositeOp::categoryLight()) { }
 
 public:
-    inline static channels_type selectAlpha(channels_type srcAlpha, channels_type dstAlpha) {
-        return qMin(srcAlpha, dstAlpha);
-    }
-    inline static void composeColorChannels(channels_type srcBlend,
-                                            const channels_type* src,
-                                            channels_type* dst,
-                                            bool allChannelFlags,
-                                            const QBitArray & channelFlags) {
-        for (uint channel = 0; channel < _CSTraits::channels_nb; channel++) {
-            if ((int)channel != _CSTraits::alpha_pos && (allChannelFlags || channelFlags.testBit(channel))) {
-                compositetype srcColor = src[channel];
-                compositetype dstColor = dst[channel];
-
-                srcColor = qMin((compositetype)(dstColor * (NATIVE_MAX_VALUE + 1)) / (NATIVE_MAX_VALUE + 1 - srcColor), (compositetype)NATIVE_MAX_VALUE);
-
-                channels_type newColor = KoColorSpaceMaths<channels_type>::blend(srcColor, dstColor, srcBlend);
-
-                dst[channel] = newColor;
+    inline static channels_type composeColorChannels(const channels_type* src, channels_type srcAlpha,
+                                                     channels_type* dst, channels_type dstAlpha,
+                                                     channels_type opacity, const QBitArray& channelFlags) {
+        srcAlpha = mul(srcAlpha, opacity);
+        
+        channels_type newDstAlpha = unionShapeOpacy(srcAlpha, dstAlpha);
+        
+        if(newDstAlpha != KoColorSpaceMathsTraits<channels_type>::zeroValue) {
+            for(qint32 i=0; i <channels_nb; i++) {
+                if(i != alpha_pos && channelFlags.testBit(i)) {
+                    channels_type result = blend(src[i], srcAlpha, dst[i], dstAlpha, &cfColorDodge<channels_type>);
+                    dst[i] = div(result, newDstAlpha);
+                }
             }
         }
+        
+        return newDstAlpha;
     }
 
 };
diff --git a/libs/pigment/compositeops/KoCompositeOpFunctions.h b/libs/pigment/compositeops/KoCompositeOpFunctions.h
new file mode 100644
index 0000000..bdd3af7
--- /dev/null
+++ b/libs/pigment/compositeops/KoCompositeOpFunctions.h
@@ -0,0 +1,193 @@
+/*
+ *  Copyright (c) 2011 Silvio Heinrich <plassy at web.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KOCOMPOSITEOPFUNCTIONS_H_
+#define KOCOMPOSITEOPFUNCTIONS_H_
+
+#include <KoColorSpaceMaths.h>
+#include <KoColorSpaceConstants.h>
+#include <KoCompositeOp.h>
+
+/* --------------------- Arithmetic functions ----------------------------- /
+ * definitions of standard arithmetic functions. all computations are meant
+ * to be performed on normalized values (in the range of 0.0 - 1.0)
+ * if non floating point types are used, fixed point arithmetic is used
+ */
+
+template<class T>
+inline T mul(T a, T b) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    return T(composite_type(a) * composite_type(b) / KoColorSpaceMathsTraits<T>::unitValue);
+}
+
+template<class T>
+inline T mul(T a, T b, T c) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    return T((composite_type(a) * b * c) / (composite_type(KoColorSpaceMathsTraits<T>::unitValue) * KoColorSpaceMathsTraits<T>::unitValue));
+}
+
+template<class T>
+inline typename KoColorSpaceMathsTraits<T>::compositetype
+div(T a, T b) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    return composite_type(a) * KoColorSpaceMathsTraits<T>::unitValue / composite_type(b);
+}
+
+template<class T>
+inline T inv(T a) {
+    return KoColorSpaceMathsTraits<T>::unitValue - a;
+}
+
+template<class T>
+inline T clamp(typename KoColorSpaceMathsTraits<T>::compositetype a) {
+    return (a > KoColorSpaceMathsTraits<T>::unitValue) ? KoColorSpaceMathsTraits<T>::unitValue : T(a);
+}
+
+template<class T>
+inline T lerp(T a, T b, T alpha) {
+    return KoColorSpaceMaths<T>::blend(b, a, alpha);
+}
+
+template<class TRet, class T>
+inline TRet scale(T a) {
+    typedef typename KoColorSpaceMathsTraits<TRet>::compositetype composite_type;
+    return TRet(composite_type(a) * KoColorSpaceMathsTraits<TRet>::unitValue / KoColorSpaceMathsTraits<T>::unitValue);
+}
+
+/* ---------------- Blending/Compositing functions ------------------------ /
+ * definitions of standard blending/compositing functions compatible
+ * to the ISO 32000-1 specification (for PDF filed) which also defines
+ * the compositing functions who are also used in Adobe Photoshop (c)
+ */
+
+template<class T>
+inline T unionShapeOpacy(T a, T b) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    return T(composite_type(a) + b - mul(a,b));
+}
+
+template<class T, class TFunc>
+inline T blend(T src, T srcAlpha, T dst, T dstAlpha, TFunc blendFunc) {
+    return mul(inv(srcAlpha), dstAlpha, dst) + mul(inv(dstAlpha), srcAlpha, src) + mul(dstAlpha, srcAlpha, blendFunc(src, dst));
+}
+
+template<class T>
+inline T cfColorBurn(T src, T dst) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    
+    if(src != KoColorSpaceMathsTraits<T>::zeroValue)
+        return inv(clamp<T>(div(inv(dst), src)));
+    
+    return KoColorSpaceMathsTraits<T>::zeroValue;
+}
+
+template<class T>
+inline T cfColorDodge(T src, T dst) {
+    typedef typename KoColorSpaceMathsTraits<T>::compositetype composite_type;
+    
+    if(src != KoColorSpaceMathsTraits<T>::unitValue)
+        return clamp<T>(div(dst, inv(src)));
+    
+    return KoColorSpaceMathsTraits<T>::unitValue;
+}
+
+/**
+ * A template base class that can be used for most composite modes/ops
+ *
+ * @param _compositeOp this template parameter is a class that must be
+ *        derived fom KoCompositeOpBase and must define the static member function
+ *        inline static channels_type composeColorChannels(
+ *            const channels_type* src,
+ *            channels_type srcAlpha,
+ *            channels_type* dst,
+ *            channels_type dstAlpha,
+ *            channels_type opacity,
+ *            const QBitArray& channelFlags
+ *        )
+ *
+ *        where channels_type is _CSTraits::channels_type
+ */
+template<class _CSTraits, class _compositeOp>
+class KoCompositeOpBase : public KoCompositeOp
+{
+    typedef typename _CSTraits::channels_type channels_type;
+    static const qint32 channels_nb = _CSTraits::channels_nb;
+    static const qint32 alpha_pos   = _CSTraits::alpha_pos;
+    
+public:
+    
+    KoCompositeOpBase(const KoColorSpace* cs, const QString& id, const QString& description, const QString& category)
+        : KoCompositeOp(cs, id, description, category) { }
+    
+private:
+    template<bool alphaLocked>
+    void genericComposite(quint8*       dstRowStart , qint32 dstRowStride ,
+                          const quint8* srcRowStart , qint32 srcRowStride ,
+                          const quint8* maskRowStart, qint32 maskRowStride,
+                          qint32 rows, qint32 cols, quint8 U8_opacity, const QBitArray& channelFlags) const {
+        
+        qint32        srcInc    = (srcRowStride == 0) ? 0 : channels_nb;
+        bool          useMask   = maskRowStart != 0;
+        channels_type unitValue = KoColorSpaceMathsTraits<channels_type>::unitValue;
+        channels_type opacity   = KoColorSpaceMaths<quint8,channels_type>::scaleToA(U8_opacity);
+        
+        for(; rows>0; --rows) {
+            const channels_type* src  = reinterpret_cast<const channels_type*>(srcRowStart);
+            channels_type*       dst  = reinterpret_cast<channels_type*>(dstRowStart);
+            const quint8*        mask = maskRowStart;
+            
+            for(qint32 c=cols; c>0; --c) {
+                channels_type srcAlpha    = (alpha_pos == -1) ? unitValue : src[alpha_pos];
+                channels_type dstAlpha    = (alpha_pos == -1) ? unitValue : dst[alpha_pos];
+                channels_type blend       = useMask ? mul(opacity, scale<channels_type>(*mask)) : opacity;
+                channels_type newDstAlpha = _compositeOp::composeColorChannels(src, srcAlpha, dst, dstAlpha, blend, channelFlags);
+                
+                if(alpha_pos != -1)
+                    dst[alpha_pos] = alphaLocked ? dstAlpha : newDstAlpha;
+                
+                src += srcInc;
+                dst += channels_nb;
+                ++mask;
+            }
+            
+            srcRowStart  += srcRowStride;
+            dstRowStart  += dstRowStride;
+            maskRowStart += maskRowStride;
+        }
+    }
+
+public:
+    using KoCompositeOp::composite;
+    
+    virtual void composite(quint8*       dstRowStart , qint32 dstRowStride ,
+                           const quint8* srcRowStart , qint32 srcRowStride ,
+                           const quint8* maskRowStart, qint32 maskRowStride,
+                           qint32 rows, qint32 cols, quint8 U8_opacity, const QBitArray& channelFlags) const {
+        
+        const QBitArray& flags       = channelFlags.isEmpty() ? QBitArray(channels_nb,true) : channelFlags;
+        bool             alphaLocked = (alpha_pos != -1) && !flags.testBit(alpha_pos);
+        
+        if(alphaLocked)
+            genericComposite<true>(dstRowStart, dstRowStride, srcRowStart, srcRowStride, maskRowStart, maskRowStride, rows, cols, U8_opacity, flags);
+        else
+            genericComposite<false>(dstRowStart, dstRowStride, srcRowStart, srcRowStride, maskRowStart, maskRowStride, rows, cols, U8_opacity, flags);
+    }
+};
+
+#endif // KOCOMPOSITEOPFUNCTIONS_H_
-- 
1.7.1




More information about the kimageshop mailing list