Warning, file /office/calligra/libs/pigment/KoConvolutionOpImpl.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *  Copyright (c) 2006 Cyrille Berger <cberger@cberger.net>
0003  *  Copyright (c) 2007 Emanuele Tamponi <emanuele@valinor.it>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2.1 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Lesser General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Lesser General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #ifndef KO_CONVOLUTION_OP_IMPL_H
0022 #define KO_CONVOLUTION_OP_IMPL_H
0023 
0024 #include "DebugPigment.h"
0025 #include "KoColorSpaceMaths.h"
0026 #include "KoConvolutionOp.h"
0027 #include "KoColorSpaceTraits.h"
0028 
0029 template<class _CSTrait>
0030 class KoConvolutionOpImpl : public KoConvolutionOp
0031 {
0032     typedef typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype compositetype;
0033     typedef typename _CSTrait::channels_type channels_type;
0034 public:
0035 
0036     KoConvolutionOpImpl() { }
0037 
0038     ~KoConvolutionOpImpl() override { }
0039 
0040     /**
0041      * Calculates a weighted average of the pixels, mentioned in @p colors
0042      * using weight values from @p kernelValues
0043      *
0044      * Note:
0045      * It behaves in a quite unclear way, when at least one pixel is
0046      * fully transparent. There are three cases:
0047      * Case A) None of the pixels is fully transparent.
0048      *    * Every color channel AND alpha channel of @p dst stores a sum
0049      *      of the corresponding channels from @p colors, divided by @p factor
0050      *      and incremented by @p offset
0051      * Case B) At least one pixel of @p colors is transparent and @p factor
0052      * stores a weight of the kernel (sum of it's items).
0053      *    * Every color channel of @p dst stores a sum of the corresponding
0054      *      channels from non-transparent pixels, divided by a weight
0055      *      of non-transparent pixels and incremented by @p offset.
0056      *    * Alpha channel of @p dst stores a sum of the corresponding
0057      *      channels from non-transparent pixels, divided by a weight
0058      *      of all the pixels (equals to @p factor) and incremented
0059      *      by @p offset.
0060      * Case C) At least one pixel of @p colors is transparent and @p factor
0061      * is set to an arbitrary value.
0062      *    * Every color channel of @p dst stores a sum of the corresponding
0063      *      channels from non-transparent pixels, divided by a "scaled
0064      *      down factor" and incremented by @p offset. "Scaled
0065      *      down factor" is calculated in the following way:
0066      *
0067      *                                   [weight of non-transparent pixels]
0068      *      scaledDownFactor = @p factor * ----------------------------------
0069      *                                       [weight of all the pixels]
0070      *
0071      *    * Alpha channel of @p dst stores a sum of the corresponding
0072      *      channels from non-transparent pixels, divided by unscaled
0073      *      @p factor and incremented by @p offset.
0074      */
0075 
0076     void convolveColors(const quint8* const* colors, const qreal* kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nPixels, const QBitArray & channelFlags) const override {
0077 
0078         // Create and initialize to 0 the array of totals
0079         qreal totals[_CSTrait::channels_nb];
0080 
0081         qreal totalWeight = 0;
0082         qreal totalWeightTransparent = 0;
0083 
0084         memset(totals, 0, sizeof(qreal) * _CSTrait::channels_nb);
0085 
0086         for (; nPixels--; colors++, kernelValues++) {
0087             qreal weight = *kernelValues;
0088             const channels_type* color = _CSTrait::nativeArray(*colors);
0089             if (weight != 0) {
0090                 if (_CSTrait::opacityU8(*colors) == 0) {
0091                     totalWeightTransparent += weight;
0092                 } else {
0093                     for (uint i = 0; i < _CSTrait::channels_nb; i++) {
0094                         totals[i] += color[i] * weight;
0095                     }
0096                 }
0097                 totalWeight += weight;
0098             }
0099         }
0100 
0101         typename _CSTrait::channels_type* dstColor = _CSTrait::nativeArray(dst);
0102 
0103         bool allChannels = channelFlags.isEmpty();
0104         Q_ASSERT(allChannels || channelFlags.size() == (int)_CSTrait::channels_nb);
0105         if (totalWeightTransparent == 0) {
0106             // Case A)
0107             for (uint i = 0; i < _CSTrait::channels_nb; i++) {
0108                 if (allChannels || channelFlags.testBit(i)) {
0109                     compositetype v = totals[i] / factor + offset;
0110                     dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
0111                                           KoColorSpaceMathsTraits<channels_type>::max);
0112                 }
0113             }
0114         } else if (totalWeightTransparent != totalWeight) {
0115             if (totalWeight == factor) {
0116                 // Case B)
0117                 qint64 a = (totalWeight - totalWeightTransparent);
0118                 for (uint i = 0; i < _CSTrait::channels_nb; i++) {
0119                     if (allChannels || channelFlags.testBit(i)) {
0120                         if (i == (uint)_CSTrait::alpha_pos) {
0121                             compositetype v = totals[i] / totalWeight + offset;
0122                             dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
0123                                                   KoColorSpaceMathsTraits<channels_type>::max);
0124                         } else {
0125                             compositetype v = totals[i] / a + offset;
0126                             dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
0127                                                   KoColorSpaceMathsTraits<channels_type>::max);
0128                         }
0129                     }
0130                 }
0131             } else {
0132                 // Case C)
0133                 qreal a = qreal(totalWeight) / (factor * (totalWeight - totalWeightTransparent));     // use qreal as it easily saturate
0134                 for (uint i = 0; i < _CSTrait::channels_nb; i++) {
0135                     if (allChannels || channelFlags.testBit(i)) {
0136                         if (i == (uint)_CSTrait::alpha_pos) {
0137                             compositetype v = totals[i] / factor + offset;
0138                             dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
0139                                                   KoColorSpaceMathsTraits<channels_type>::max);
0140                         } else {
0141                             compositetype v = (compositetype)(totals[i] * a + offset);
0142                             dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min,
0143                                                   KoColorSpaceMathsTraits<channels_type>::max);
0144                         }
0145                     }
0146                 }
0147             }
0148         }
0149 
0150     }
0151 };
0152 
0153 #endif