File indexing completed on 2024-05-26 04:33:07

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Manuel Riecke <spell1337@gmail.com>
0003  *
0004  * SPDX-License-Identifier: ICS
0005  */
0006 
0007 #include "indexcolorpalette.h"
0008 
0009 #include <qmath.h>
0010 
0011 #include <KoColorSpaceMaths.h>
0012 #include <KoColorSpaceRegistry.h>
0013 #include <filter/kis_filter_configuration.h>
0014 #include <widgets/kis_multi_integer_filter_widget.h>
0015 
0016 float IndexColorPalette::similarity(LabColor c0, LabColor c1) const
0017 {
0018     static const qreal max = KoColorSpaceMathsTraits<quint16>::max;
0019     quint16 diffL = qAbs(c0.L - c1.L);
0020     quint16 diffa = qAbs(c0.a - c1.a);
0021     quint16 diffb = qAbs(c0.b - c1.b);
0022     float valL = diffL/max*similarityFactors.L;
0023     float valA = diffa/max*similarityFactors.a;
0024     float valB = diffb/max*similarityFactors.b;
0025     return 1.f - qSqrt(valL * valL + valA * valA + valB * valB);
0026 }
0027 
0028 IndexColorPalette::IndexColorPalette()
0029 {
0030     similarityFactors.L = 1.0f;
0031     similarityFactors.a = 1.0f;
0032     similarityFactors.b = 1.0f;
0033 }
0034 
0035 int IndexColorPalette::numColors() const
0036 {
0037     return colors.size();
0038 }
0039 
0040 LabColor IndexColorPalette::getNearestIndex(LabColor clr) const
0041 {
0042     QVector<float> diffs;
0043     diffs.resize(numColors());
0044     for(int i = 0; i < numColors(); ++i)
0045         diffs[i] = similarity(colors[i], clr);
0046 
0047     int primaryColor = -1;
0048     float maxDiff = std::numeric_limits<float>::min();
0049     for(int i = 0; i < numColors(); ++i)
0050         if(diffs[i] > maxDiff) {
0051             primaryColor = i;
0052             maxDiff = diffs[primaryColor];
0053         }
0054 
0055     KIS_SAFE_ASSERT_RECOVER (primaryColor >= 0) {
0056         LabColor color;
0057         color.L = 0;
0058         color.a = 0;
0059         color.b = 0;
0060 
0061         return color;
0062     }
0063 
0064     return colors[primaryColor];
0065 }
0066 
0067 QPair<int, int> IndexColorPalette::getNeighbours(int mainClr) const
0068 {
0069     QVector<float> diffs;
0070     diffs.resize(numColors());
0071     for(int i = 0; i < numColors(); ++i)
0072         diffs[i] = similarity(colors[i], colors[mainClr]);
0073 
0074     int darkerColor = 0;
0075     int brighterColor = 0;
0076     for(int i = 0; i < numColors(); ++i)
0077     {
0078         if(i != mainClr)
0079         {
0080             if(colors[i].L < colors[mainClr].L)
0081             {
0082                 if(diffs[i] > diffs[darkerColor])
0083                     darkerColor = i;
0084             }
0085             else
0086             {
0087                 if(diffs[i] > diffs[brighterColor])
0088                     brighterColor = i;
0089             }
0090         }
0091     }
0092 
0093     return qMakePair(darkerColor, brighterColor);
0094 }
0095 
0096 void IndexColorPalette::insertShades(LabColor clrA, LabColor clrB, int shades)
0097 {
0098     if(shades == 0) return;
0099     qint16  lumaStep = (clrB.L - clrA.L) / (shades+1);
0100     qint16 astarStep = (clrB.a - clrA.a) / (shades+1);
0101     qint16 bstarStep = (clrB.b - clrA.b) / (shades+1);
0102     for(int i = 0; i < shades; ++i)
0103     {
0104         clrA.L += lumaStep;
0105         clrA.a += astarStep;
0106         clrA.b += bstarStep;
0107         insertColor(clrA);
0108     }
0109 }
0110 
0111 void IndexColorPalette::insertShades(KoColor koclrA, KoColor koclrB, int shades)
0112 {
0113     koclrA.convertTo(KoColorSpaceRegistry::instance()->lab16());
0114     koclrB.convertTo(KoColorSpaceRegistry::instance()->lab16());
0115     LabColor clrA = *(reinterpret_cast<LabColor*>(koclrA.data()));
0116     LabColor clrB = *(reinterpret_cast<LabColor*>(koclrB.data()));
0117     insertShades(clrA, clrB, shades);
0118 }
0119 
0120 void IndexColorPalette::insertShades(QColor qclrA, QColor qclrB, int shades)
0121 {
0122     KoColor koclrA;
0123     koclrA.fromQColor(qclrA);
0124     koclrA.convertTo(KoColorSpaceRegistry::instance()->lab16());
0125     KoColor koclrB;
0126     koclrB.fromQColor(qclrB);
0127     koclrB.convertTo(KoColorSpaceRegistry::instance()->lab16());
0128     LabColor clrA = *(reinterpret_cast<LabColor*>(koclrA.data()));
0129     LabColor clrB = *(reinterpret_cast<LabColor*>(koclrB.data()));
0130     insertShades(clrA, clrB, shades);
0131 }
0132 
0133 void IndexColorPalette::insertColor(LabColor clr)
0134 {
0135     colors.append(clr);
0136 }
0137 
0138 void IndexColorPalette::insertColor(KoColor koclr)
0139 {
0140     koclr.convertTo(KoColorSpaceRegistry::instance()->lab16());
0141     LabColor clr = *(reinterpret_cast<LabColor*>(koclr.data()));
0142     insertColor(clr);
0143 }
0144 
0145 void IndexColorPalette::insertColor(QColor qclr)
0146 {
0147     KoColor koclr;
0148     koclr.fromQColor(qclr);
0149     koclr.convertTo(KoColorSpaceRegistry::instance()->lab16());
0150     LabColor clr = *(reinterpret_cast<LabColor*>(koclr.data()));
0151     insertColor(clr);
0152 }
0153 
0154 namespace
0155 {
0156     struct ColorString
0157     {
0158         int color;
0159         QPair<int, int> neighbours;
0160         float similarity;
0161     };
0162 }
0163 
0164 void IndexColorPalette::mergeMostRedundantColors()
0165 {
0166     QVector<ColorString> colorHood;
0167     colorHood.resize(numColors());
0168     for(int i = 0; i < numColors(); ++i)
0169     {
0170         colorHood[i].color = i;
0171         colorHood[i].neighbours = getNeighbours(i);
0172         float lSimilarity = 0.05f, rSimilarity = 0.05f;
0173         // There will be exactly 2 colors that have only 1 neighbour, the darkest and the brightest, we don't want to remove those
0174         if(colorHood[i].neighbours.first  != -1)
0175             lSimilarity = similarity(colors[colorHood[i].neighbours.first], colors[i]);
0176         if(colorHood[i].neighbours.second != -1)
0177             rSimilarity = similarity(colors[colorHood[i].neighbours.second], colors[i]);
0178         colorHood[i].similarity = (lSimilarity + rSimilarity) / 2;
0179     }
0180     int mostSimilarColor = 0;
0181     for(int i = 0; i < numColors(); ++i)
0182         if(colorHood[i].similarity > colorHood[mostSimilarColor].similarity)
0183             mostSimilarColor = i;
0184 
0185     int darkerIndex = colorHood[mostSimilarColor].neighbours.first;
0186     int brighterIndex = colorHood[mostSimilarColor].neighbours.second;
0187     if(darkerIndex   != -1 &&
0188        brighterIndex != -1)
0189     {
0190         LabColor clrA = colors[darkerIndex];
0191         LabColor clrB = colors[mostSimilarColor];
0192         // Remove two, add one = 1 color less
0193         colors.remove(darkerIndex);
0194         colors.remove(mostSimilarColor);
0195         //colors.remove(brighterIndex);
0196         insertShades(clrA, clrB, 1);
0197         //insertShades(clrB, clrC, 1);
0198     }
0199 }