Visitar URL original
PaletteGenerator: Optimize histogram creation (#67) · flutter/packages@9700377 · GitHub
Skip to content

Commit 9700377

Browse files
authored
PaletteGenerator: Optimize histogram creation (#67)
PaletteGenerator: Optimize histogram creation
1 parent adaf7f9 commit 9700377

1 file changed

Lines changed: 93 additions & 10 deletions

File tree

‎packages/palette_generator/lib/palette_generator.dart‎

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:collection';
67
import 'dart:math' as math;
78
import 'dart:typed_data';
89
import 'dart:ui' as ui;
@@ -838,7 +839,7 @@ class _ColorVolumeBox {
838839
_fitMinimumBox();
839840
}
840841

841-
final Map<Color, int> histogram;
842+
final _ColorHistogram histogram;
842843
final List<Color> colors;
843844

844845
// The lower and upper index are inclusive.
@@ -883,7 +884,7 @@ class _ColorVolumeBox {
883884
int count = 0;
884885
for (int i = _lowerIndex; i <= _upperIndex; i++) {
885886
final Color color = colors[i];
886-
count += histogram[color];
887+
count += histogram[color].value;
887888
if (color.red > maxRed) {
888889
maxRed = color.red;
889890
}
@@ -981,7 +982,7 @@ class _ColorVolumeBox {
981982
colors.replaceRange(_lowerIndex, _upperIndex + 1, colorSubset);
982983
final int median = (_population / 2).round();
983984
for (int i = 0, count = 0; i <= colorSubset.length; i++) {
984-
count += histogram[colorSubset[i]];
985+
count += histogram[colorSubset[i]].value;
985986
if (count >= median) {
986987
// We never want to split on the upperIndex, as this will result in the
987988
// same box.
@@ -998,7 +999,7 @@ class _ColorVolumeBox {
998999
int totalPopulation = 0;
9991000
for (int i = _lowerIndex; i <= _upperIndex; i++) {
10001001
final Color color = colors[i];
1001-
final int colorPopulation = histogram[color];
1002+
final int colorPopulation = histogram[color].value;
10021003
totalPopulation += colorPopulation;
10031004
redSum += colorPopulation * color.red;
10041005
greenSum += colorPopulation * color.green;
@@ -1014,6 +1015,77 @@ class _ColorVolumeBox {
10141015
}
10151016
}
10161017

1018+
/// Holds mutable count for a color.
1019+
// Using a mutable count rather than replacing value in the histogram
1020+
// in the _ColorCutQuantizer speeds up building the histogram significantly.
1021+
class _ColorCount {
1022+
int value = 0;
1023+
}
1024+
1025+
class _ColorHistogram {
1026+
final Map<int, Map<int, Map<int, _ColorCount>>> _hist =
1027+
<int, Map<int, Map<int, _ColorCount>>>{};
1028+
final DoubleLinkedQueue<Color> _keys = DoubleLinkedQueue<Color>();
1029+
1030+
_ColorCount operator [](Color color) {
1031+
final Map<int, Map<int, _ColorCount>> redMap = _hist[color.red];
1032+
if (redMap == null) {
1033+
return null;
1034+
}
1035+
final Map<int, _ColorCount> blueMap = redMap[color.blue];
1036+
if (blueMap == null) {
1037+
return null;
1038+
}
1039+
return blueMap[color.green];
1040+
}
1041+
1042+
void operator []=(Color key, _ColorCount value) {
1043+
final int red = key.red;
1044+
final int blue = key.blue;
1045+
final int green = key.green;
1046+
1047+
bool newColor = false;
1048+
1049+
Map<int, Map<int, _ColorCount>> redMap = _hist[red];
1050+
if (redMap == null) {
1051+
_hist[red] = redMap = <int, Map<int, _ColorCount>>{};
1052+
newColor = true;
1053+
}
1054+
1055+
Map<int, _ColorCount> blueMap = redMap[blue];
1056+
if (blueMap == null) {
1057+
redMap[blue] = blueMap = <int, _ColorCount>{};
1058+
newColor = true;
1059+
}
1060+
1061+
if (blueMap[green] == null) {
1062+
newColor = true;
1063+
}
1064+
blueMap[green] = value;
1065+
1066+
if (newColor) {
1067+
_keys.add(key);
1068+
}
1069+
}
1070+
1071+
void removeWhere(bool predicate(Color key)) {
1072+
for (Color key in _keys) {
1073+
if (predicate(key)) {
1074+
_hist[key.red][key.blue][key.green] = null;
1075+
}
1076+
}
1077+
_keys.removeWhere((Color color) => predicate(color));
1078+
}
1079+
1080+
Iterable<Color> get keys {
1081+
return _keys;
1082+
}
1083+
1084+
int get length {
1085+
return _keys.length;
1086+
}
1087+
}
1088+
10171089
class _ColorCutQuantizer {
10181090
_ColorCutQuantizer(
10191091
this.image, {
@@ -1108,28 +1180,39 @@ class _ColorCutQuantizer {
11081180
await image.toByteData(format: ui.ImageByteFormat.rawRgba);
11091181
final Iterable<Color> pixels =
11101182
_getImagePixels(imageData, image.width, image.height, region: region);
1111-
final Map<Color, int> hist = <Color, int>{};
1183+
final _ColorHistogram hist = _ColorHistogram();
1184+
Color currentColor;
1185+
_ColorCount currentColorCount;
1186+
11121187
for (Color pixel in pixels) {
11131188
// Update the histogram, but only for non-zero alpha values, and for the
11141189
// ones we do add, make their alphas opaque so that we can use a Color as
11151190
// the histogram key.
11161191
final Color quantizedColor = quantizeColor(pixel);
11171192
final Color colorKey = quantizedColor.withAlpha(0xff);
11181193
// Skip pixels that are entirely transparent.
1119-
if (quantizedColor.alpha != 0x0) {
1120-
hist[colorKey] = (hist[colorKey] ?? 0) + 1;
1194+
if (quantizedColor.alpha == 0x0) {
1195+
continue;
1196+
}
1197+
if (currentColor != colorKey) {
1198+
currentColor = colorKey;
1199+
currentColorCount = hist[colorKey];
1200+
if (currentColorCount == null) {
1201+
hist[colorKey] = currentColorCount = _ColorCount();
1202+
}
11211203
}
1204+
currentColorCount.value = currentColorCount.value + 1;
11221205
}
11231206
// Now let's remove any colors that the filters want to ignore.
1124-
hist.removeWhere((Color color, int _) {
1207+
hist.removeWhere((Color color) {
11251208
return _shouldIgnoreColor(color);
11261209
});
11271210
if (hist.length <= maxColors) {
11281211
// The image has fewer colors than the maximum requested, so just return
11291212
// the colors.
11301213
_paletteColors.clear();
11311214
for (Color color in hist.keys) {
1132-
_paletteColors.add(PaletteColor(color, hist[color]));
1215+
_paletteColors.add(PaletteColor(color, hist[color].value));
11331216
}
11341217
} else {
11351218
// We need use quantization to reduce the number of colors
@@ -1141,7 +1224,7 @@ class _ColorCutQuantizer {
11411224

11421225
List<PaletteColor> _quantizePixels(
11431226
int maxColors,
1144-
Map<Color, int> histogram,
1227+
_ColorHistogram histogram,
11451228
) {
11461229
int volumeComparator(_ColorVolumeBox a, _ColorVolumeBox b) {
11471230
return b.getVolume().compareTo(a.getVolume());

0 commit comments

Comments
 (0)