File indexing completed on 2024-05-19 04:26:41
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #ifndef KISCOLORSELECTIONPOLICIES 0008 #define KISCOLORSELECTIONPOLICIES 0009 0010 #include <QStack> 0011 0012 #include <KoAlwaysInline.h> 0013 #include <KoColor.h> 0014 #include <KoColorSpace.h> 0015 #include <kis_random_accessor_ng.h> 0016 0017 namespace KisColorSelectionPolicies 0018 { 0019 0020 class SlowDifferencePolicy 0021 { 0022 public: 0023 SlowDifferencePolicy(const KoColor &referenceColor, int threshold) 0024 : m_colorSpace(referenceColor.colorSpace()) 0025 , m_referenceColor(referenceColor) 0026 , m_referenceColorPtr(m_referenceColor.data()) 0027 , m_referenceColorIsTransparent(m_colorSpace->opacityU8(m_referenceColorPtr) == OPACITY_TRANSPARENT_U8) 0028 , m_threshold(threshold) 0029 {} 0030 0031 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0032 { 0033 if (m_threshold == 1) { 0034 const bool colorIsTransparent = (m_colorSpace->opacityU8(colorPtr) == OPACITY_TRANSPARENT_U8); 0035 if ((m_referenceColorIsTransparent && colorIsTransparent) || 0036 memcmp(m_referenceColorPtr, colorPtr, m_colorSpace->pixelSize()) == 0) { 0037 return 0; 0038 } 0039 return quint8_MAX; 0040 } 0041 else { 0042 return m_colorSpace->differenceA(m_referenceColorPtr, colorPtr); 0043 } 0044 } 0045 0046 protected: 0047 const KoColorSpace *m_colorSpace; 0048 KoColor m_referenceColor; 0049 const quint8 *m_referenceColorPtr; 0050 const bool m_referenceColorIsTransparent; 0051 int m_threshold; 0052 }; 0053 0054 template <typename SrcPixelType> 0055 class OptimizedDifferencePolicy : public SlowDifferencePolicy 0056 { 0057 public: 0058 OptimizedDifferencePolicy(const KoColor &referenceColor, int threshold) 0059 : SlowDifferencePolicy(referenceColor, threshold) 0060 {} 0061 0062 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0063 { 0064 HashKeyType key = *reinterpret_cast<const HashKeyType*>(colorPtr); 0065 0066 quint8 result; 0067 0068 typename HashType::iterator it = m_differences.find(key); 0069 0070 if (it != m_differences.end()) { 0071 result = *it; 0072 } else { 0073 result = SlowDifferencePolicy::difference(colorPtr); 0074 m_differences.insert(key, result); 0075 } 0076 0077 return result; 0078 } 0079 0080 protected: 0081 using HashKeyType = SrcPixelType; 0082 using HashType = QHash<HashKeyType, quint8>; 0083 0084 mutable HashType m_differences; 0085 }; 0086 0087 class SlowColorOrTransparentDifferencePolicy : public SlowDifferencePolicy 0088 { 0089 public: 0090 SlowColorOrTransparentDifferencePolicy(const KoColor &referenceColor, int threshold) 0091 : SlowDifferencePolicy(referenceColor, threshold) 0092 {} 0093 0094 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0095 { 0096 if (m_threshold == 1) { 0097 if (memcmp(m_referenceColorPtr, colorPtr, m_colorSpace->pixelSize()) == 0 || 0098 m_colorSpace->opacityU8(colorPtr) == 0) { 0099 return 0; 0100 } 0101 return quint8_MAX; 0102 } 0103 else { 0104 const quint8 colorDifference = m_colorSpace->difference(m_referenceColorPtr, colorPtr); 0105 const quint8 opacityDifference = m_colorSpace->opacityU8(colorPtr) * 100 / quint8_MAX; 0106 return qMin(colorDifference, opacityDifference); 0107 } 0108 } 0109 }; 0110 0111 template <typename SrcPixelType> 0112 class OptimizedColorOrTransparentDifferencePolicy : public OptimizedDifferencePolicy<SrcPixelType> 0113 { 0114 public: 0115 OptimizedColorOrTransparentDifferencePolicy(const KoColor &referenceColor, int threshold) 0116 : OptimizedDifferencePolicy<SrcPixelType>(referenceColor, threshold) 0117 {} 0118 0119 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0120 { 0121 HashKeyType key = *reinterpret_cast<const HashKeyType*>(colorPtr); 0122 0123 quint8 result; 0124 0125 typename HashType::iterator it = this->m_differences.find(key); 0126 0127 if (it != this->m_differences.end()) { 0128 result = *it; 0129 } else { 0130 const quint8 colorDifference = this->m_colorSpace->difference(this->m_referenceColorPtr, colorPtr); 0131 const quint8 opacityDifference = this->m_colorSpace->opacityU8(colorPtr) * 100 / quint8_MAX; 0132 result = qMin(colorDifference, opacityDifference); 0133 this->m_differences.insert(key, result); 0134 } 0135 0136 return result; 0137 } 0138 0139 protected: 0140 using HashKeyType = typename OptimizedDifferencePolicy<SrcPixelType>::HashKeyType; 0141 using HashType = typename OptimizedDifferencePolicy<SrcPixelType>::HashType; 0142 }; 0143 0144 class SlowIsNonNullDifferencePolicy 0145 { 0146 public: 0147 SlowIsNonNullDifferencePolicy(int pixelSize) 0148 : m_pixelSize(pixelSize) 0149 , m_testColor(pixelSize, 0) 0150 {} 0151 0152 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0153 { 0154 if (memcmp(m_testColor.data(), colorPtr, m_pixelSize) == 0) { 0155 return 0; 0156 } 0157 return quint8_MAX; 0158 } 0159 0160 private: 0161 int m_pixelSize {0}; 0162 QByteArray m_testColor; 0163 }; 0164 0165 template <typename SrcPixelType> 0166 class OptimizedIsNonNullDifferencePolicy 0167 { 0168 public: 0169 ALWAYS_INLINE quint8 difference(const quint8 *colorPtr) const 0170 { 0171 const SrcPixelType *pixel = reinterpret_cast<const SrcPixelType*>(colorPtr); 0172 return *pixel == 0; 0173 } 0174 }; 0175 0176 class HardSelectionPolicy 0177 { 0178 public: 0179 HardSelectionPolicy(int threshold) : m_threshold(threshold) {} 0180 0181 ALWAYS_INLINE quint8 opacityFromDifference(quint8 difference) const 0182 { 0183 return difference <= m_threshold ? MAX_SELECTED : MIN_SELECTED; 0184 } 0185 0186 protected: 0187 int m_threshold; 0188 }; 0189 0190 class SoftSelectionPolicy : public HardSelectionPolicy 0191 { 0192 public: 0193 SoftSelectionPolicy(int threshold, int softness) 0194 : HardSelectionPolicy(threshold) 0195 , m_softness(softness) 0196 {} 0197 0198 ALWAYS_INLINE quint8 opacityFromDifference(quint8 difference) const 0199 { 0200 if (m_threshold == 0) { 0201 return MIN_SELECTED; 0202 } 0203 // Integer version of: (threshold - diff) / (threshold * softness) 0204 if (difference < m_threshold) { 0205 const int v = (m_threshold - difference) * MAX_SELECTED * 100 / (m_threshold * m_softness); 0206 return v > MAX_SELECTED ? MAX_SELECTED : v; 0207 } else { 0208 return MIN_SELECTED; 0209 } 0210 } 0211 0212 protected: 0213 int m_softness; 0214 }; 0215 0216 class SelectAllUntilColorHardSelectionPolicy: public HardSelectionPolicy 0217 { 0218 public: 0219 SelectAllUntilColorHardSelectionPolicy(int threshold) 0220 : HardSelectionPolicy(threshold) 0221 {} 0222 0223 ALWAYS_INLINE quint8 opacityFromDifference(quint8 difference) const 0224 { 0225 return difference > m_threshold ? MAX_SELECTED : MIN_SELECTED; 0226 } 0227 }; 0228 0229 class SelectAllUntilColorSoftSelectionPolicy : public SoftSelectionPolicy 0230 { 0231 public: 0232 SelectAllUntilColorSoftSelectionPolicy(int threshold, int softness) 0233 : SoftSelectionPolicy(threshold, softness) 0234 {} 0235 0236 ALWAYS_INLINE quint8 opacityFromDifference(quint8 difference) const 0237 { 0238 if (m_threshold == 0) { 0239 return MAX_SELECTED; 0240 } 0241 // Integer version of: 1 - ((1-threshold) - diff) / ((1-threshold) * softness) 0242 if (difference < m_threshold) { 0243 const int v = MAX_SELECTED - (m_threshold - difference) * MAX_SELECTED * 100 / (m_threshold * m_softness); 0244 return v < MIN_SELECTED ? MIN_SELECTED : v; 0245 } else { 0246 return MAX_SELECTED; 0247 } 0248 } 0249 }; 0250 0251 } 0252 0253 #endif