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