File indexing completed on 2024-05-12 15:59:36
0001 /* 0002 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2007 Emanuele Tamponi <emanuele@valinor.it> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #ifndef KO_CONVOLUTION_OP_IMPL_H 0009 #define KO_CONVOLUTION_OP_IMPL_H 0010 0011 #include "DebugPigment.h" 0012 #include "KoColorSpaceMaths.h" 0013 #include "KoConvolutionOp.h" 0014 #include "KoColorSpaceTraits.h" 0015 0016 template<class _CSTrait> 0017 class KoConvolutionOpImpl : public KoConvolutionOp 0018 { 0019 typedef typename KoColorSpaceMathsTraits<typename _CSTrait::channels_type>::compositetype compositetype; 0020 typedef typename _CSTrait::channels_type channels_type; 0021 public: 0022 0023 KoConvolutionOpImpl() { } 0024 0025 ~KoConvolutionOpImpl() override { } 0026 0027 /** 0028 * Calculates a weighted average of the pixels, mentioned in @p colors 0029 * using weight values from @p kernelValues 0030 * 0031 * Note: 0032 * It behaves in a quite unclear way, when at least one pixel is 0033 * fully transparent. There are three cases: 0034 * Case A) None of the pixels is fully transparent. 0035 * * Every color channel AND alpha channel of @p dst stores a sum 0036 * of the corresponding channels from @p colors, divided by @p factor 0037 * and incremented by @p offset 0038 * Case B) At least one pixel of @p colors is transparent and @p factor 0039 * stores a weight of the kernel (sum of it's items). 0040 * * Every color channel of @p dst stores a sum of the corresponding 0041 * channels from non-transparent pixels, divided by a weight 0042 * of non-transparent pixels and incremented by @p offset. 0043 * * Alpha channel of @p dst stores a sum of the corresponding 0044 * channels from non-transparent pixels, divided by a weight 0045 * of all the pixels (equals to @p factor) and incremented 0046 * by @p offset. 0047 * Case C) At least one pixel of @p colors is transparent and @p factor 0048 * is set to an arbitrary value. 0049 * * Every color channel of @p dst stores a sum of the corresponding 0050 * channels from non-transparent pixels, divided by a "scaled 0051 * down factor" and incremented by @p offset. "Scaled 0052 * down factor" is calculated in the following way: 0053 * 0054 * [weight of non-transparent pixels] 0055 * scaledDownFactor = @p factor * ---------------------------------- 0056 * [weight of all the pixels] 0057 * 0058 * * Alpha channel of @p dst stores a sum of the corresponding 0059 * channels from non-transparent pixels, divided by unscaled 0060 * @p factor and incremented by @p offset. 0061 */ 0062 0063 void convolveColors(const quint8* const* colors, const qreal* kernelValues, quint8 *dst, qreal factor, qreal offset, qint32 nPixels, const QBitArray & channelFlags) const override { 0064 0065 // Create and initialize to 0 the array of totals 0066 qreal totals[_CSTrait::channels_nb]; 0067 0068 qreal totalWeight = 0; 0069 qreal totalWeightTransparent = 0; 0070 0071 memset(totals, 0, sizeof(qreal) * _CSTrait::channels_nb); 0072 0073 for (; nPixels--; colors++, kernelValues++) { 0074 qreal weight = *kernelValues; 0075 const channels_type* color = _CSTrait::nativeArray(*colors); 0076 if (weight != 0) { 0077 if (_CSTrait::opacityU8(*colors) == 0) { 0078 totalWeightTransparent += weight; 0079 } else { 0080 for (uint i = 0; i < _CSTrait::channels_nb; i++) { 0081 totals[i] += color[i] * weight; 0082 } 0083 } 0084 totalWeight += weight; 0085 } 0086 } 0087 0088 typename _CSTrait::channels_type* dstColor = _CSTrait::nativeArray(dst); 0089 0090 bool allChannels = channelFlags.isEmpty(); 0091 Q_ASSERT(allChannels || channelFlags.size() == (int)_CSTrait::channels_nb); 0092 if (totalWeightTransparent == 0) { 0093 // Case A) 0094 for (uint i = 0; i < _CSTrait::channels_nb; i++) { 0095 if (allChannels || channelFlags.testBit(i)) { 0096 compositetype v = totals[i] / factor + offset; 0097 dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min, 0098 KoColorSpaceMathsTraits<channels_type>::max); 0099 } 0100 } 0101 } else if (totalWeightTransparent != totalWeight) { 0102 if (totalWeight == factor) { 0103 // Case B) 0104 qint64 a = (totalWeight - totalWeightTransparent); 0105 for (uint i = 0; i < _CSTrait::channels_nb; i++) { 0106 if (allChannels || channelFlags.testBit(i)) { 0107 if (i == (uint)_CSTrait::alpha_pos) { 0108 compositetype v = totals[i] / totalWeight + offset; 0109 dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min, 0110 KoColorSpaceMathsTraits<channels_type>::max); 0111 } else { 0112 compositetype v = totals[i] / a + offset; 0113 dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min, 0114 KoColorSpaceMathsTraits<channels_type>::max); 0115 } 0116 } 0117 } 0118 } else { 0119 // Case C) 0120 qreal a = qreal(totalWeight) / (factor * (totalWeight - totalWeightTransparent)); // use qreal as it easily saturate 0121 for (uint i = 0; i < _CSTrait::channels_nb; i++) { 0122 if (allChannels || channelFlags.testBit(i)) { 0123 if (i == (uint)_CSTrait::alpha_pos) { 0124 compositetype v = totals[i] / factor + offset; 0125 dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min, 0126 KoColorSpaceMathsTraits<channels_type>::max); 0127 } else { 0128 compositetype v = (compositetype)(totals[i] * a + offset); 0129 dstColor[ i ] = CLAMP(v, KoColorSpaceMathsTraits<channels_type>::min, 0130 KoColorSpaceMathsTraits<channels_type>::max); 0131 } 0132 } 0133 } 0134 } 0135 } 0136 0137 } 0138 }; 0139 0140 #endif