37 void imageColors( QHash<QRgb, int> &colors,
const QImage &image )
40 int width = image.width();
41 int height = image.height();
43 const QRgb *currentScanLine =
nullptr;
44 QHash<QRgb, int>::iterator colorIt;
45 for (
int i = 0; i < height; ++i )
47 currentScanLine = (
const QRgb * )( image.scanLine( i ) );
48 for (
int j = 0; j < width; ++j )
50 colorIt = colors.find( currentScanLine[j] );
51 if ( colorIt == colors.end() )
53 colors.insert( currentScanLine[j], 1 );
63 bool minMaxRange(
const QgsColorBox &colorBox,
int &redRange,
int &greenRange,
int &blueRange,
int &alphaRange )
65 if ( colorBox.size() < 1 )
70 int rMin = std::numeric_limits<int>::max();
71 int gMin = std::numeric_limits<int>::max();
72 int bMin = std::numeric_limits<int>::max();
73 int aMin = std::numeric_limits<int>::max();
74 int rMax = std::numeric_limits<int>::min();
75 int gMax = std::numeric_limits<int>::min();
76 int bMax = std::numeric_limits<int>::min();
77 int aMax = std::numeric_limits<int>::min();
84 for (
auto colorBoxIt = colorBox.constBegin() ; colorBoxIt != colorBox.constEnd(); ++colorBoxIt )
86 currentRed = qRed( colorBoxIt->first );
87 if ( currentRed > rMax )
91 if ( currentRed < rMin )
96 currentGreen = qGreen( colorBoxIt->first );
97 if ( currentGreen > gMax )
101 if ( currentGreen < gMin )
106 currentBlue = qBlue( colorBoxIt->first );
107 if ( currentBlue > bMax )
111 if ( currentBlue < bMin )
116 currentAlpha = qAlpha( colorBoxIt->first );
117 if ( currentAlpha > aMax )
121 if ( currentAlpha < aMin )
127 redRange = rMax - rMin;
128 greenRange = gMax - gMin;
129 blueRange = bMax - bMin;
130 alphaRange = aMax - aMin;
134 bool redCompare( QPair<QRgb, int> c1, QPair<QRgb, int> c2 )
136 return qRed( c1.first ) < qRed( c2.first );
139 bool greenCompare( QPair<QRgb, int> c1, QPair<QRgb, int> c2 )
141 return qGreen( c1.first ) < qGreen( c2.first );
144 bool blueCompare( QPair<QRgb, int> c1, QPair<QRgb, int> c2 )
146 return qBlue( c1.first ) < qBlue( c2.first );
149 bool alphaCompare( QPair<QRgb, int> c1, QPair<QRgb, int> c2 )
151 return qAlpha( c1.first ) < qAlpha( c2.first );
154 QRgb boxColor(
const QgsColorBox &box,
int boxPixels )
165 for (
auto colorBoxIt = box.constBegin(); colorBoxIt != box.constEnd(); ++colorBoxIt )
167 currentColor = colorBoxIt->first;
168 currentPixel = colorBoxIt->second;
169 weight =
static_cast<double>( currentPixel ) / boxPixels;
170 avRed += ( qRed( currentColor ) * weight );
171 avGreen += ( qGreen( currentColor ) * weight );
172 avBlue += ( qBlue( currentColor ) * weight );
173 avAlpha += ( qAlpha( currentColor ) * weight );
176 return qRgba( avRed, avGreen, avBlue, avAlpha );
180 void splitColorBox( QgsColorBox &colorBox, QgsColorBoxMap &colorBoxMap,
181 QMap<int, QgsColorBox>::iterator colorBoxMapIt )
184 if ( colorBox.size() < 2 )
195 if ( !minMaxRange( colorBox, redRange, greenRange, blueRange, alphaRange ) )
201 if ( redRange >= greenRange && redRange >= blueRange && redRange >= alphaRange )
203 std::sort( colorBox.begin(), colorBox.end(), redCompare );
205 else if ( greenRange >= redRange && greenRange >= blueRange && greenRange >= alphaRange )
207 std::sort( colorBox.begin(), colorBox.end(), greenCompare );
209 else if ( blueRange >= redRange && blueRange >= greenRange && blueRange >= alphaRange )
211 std::sort( colorBox.begin(), colorBox.end(), blueCompare );
215 std::sort( colorBox.begin(), colorBox.end(), alphaCompare );
219 double halfSum = colorBoxMapIt.key() / 2.0;
221 int currentListIndex = 0;
223 auto colorBoxIt = colorBox.begin();
224 for ( ; colorBoxIt != colorBox.end(); ++colorBoxIt )
226 currentSum += colorBoxIt->second;
227 if ( currentSum >= halfSum )
234 if ( currentListIndex > ( colorBox.size() - 2 ) )
237 currentSum -= colorBoxIt->second;
245 QgsColorBox newColorBox1 = colorBox.mid( 0, currentListIndex + 1 );
246 colorBoxMap.insert( currentSum, newColorBox1 );
248 colorBox.erase( colorBox.begin(), colorBoxIt );
249 QgsColorBox newColorBox2 = colorBox;
250 colorBoxMap.erase( colorBoxMapIt );
251 colorBoxMap.insert( halfSum * 2.0 - currentSum, newColorBox2 );
256 void medianCut( QVector<QRgb> &colorTable,
int nColors,
const QImage &inputImage )
258 QHash<QRgb, int> inputColors;
259 imageColors( inputColors, inputImage );
261 if ( inputColors.size() <= nColors )
263 colorTable.resize( inputColors.size() );
265 for (
auto inputColorIt = inputColors.constBegin(); inputColorIt != inputColors.constEnd(); ++inputColorIt )
267 colorTable[index] = inputColorIt.key();
274 QgsColorBox firstBox;
275 int firstBoxPixelSum = 0;
276 for (
auto inputColorIt = inputColors.constBegin(); inputColorIt != inputColors.constEnd(); ++inputColorIt )
278 firstBox.push_back( qMakePair( inputColorIt.key(), inputColorIt.value() ) );
279 firstBoxPixelSum += inputColorIt.value();
282 QgsColorBoxMap colorBoxMap;
283 colorBoxMap.insert( firstBoxPixelSum, firstBox );
284 QMap<int, QgsColorBox>::iterator colorBoxMapIt = colorBoxMap.end();
287 bool allColorsMapped =
false;
288 while ( colorBoxMap.size() < nColors )
291 colorBoxMapIt = colorBoxMap.end();
295 if ( colorBoxMapIt.value().size() > 1 )
297 splitColorBox( colorBoxMapIt.value(), colorBoxMap, colorBoxMapIt );
300 if ( colorBoxMapIt == colorBoxMap.begin() )
302 allColorsMapped =
true;
307 if ( allColorsMapped )
315 colorTable.resize( colorBoxMap.size() );
316 for (
auto colorBoxIt = colorBoxMap.constBegin(); colorBoxIt != colorBoxMap.constEnd(); ++colorBoxIt )
318 colorTable[index] = boxColor( colorBoxIt.value(), colorBoxIt.key() );
QList< QPair< QRgb, int > > QgsColorBox
Median cut implementation.
void medianCut(QVector< QRgb > &colorTable, int nColors, const QImage &inputImage)
Median cut implementation used when reducing RGB colors to palletized colors.
QMultiMap< int, QgsColorBox > QgsColorBoxMap