71 std::vector<uint32_t> entries,
size_t most,
Limit how) {
75 if (entries.size() <= most)
return entries;
77 std::vector<Rgb> palette;
78 palette.reserve(entries.size());
79 for (
const auto colour : entries) palette.push_back(unpack(colour));
82 std::vector<size_t> votes(entries.size(), 0);
83 for (
size_t at = 0; at + 3 < image.
data.size(); at += 4) {
84 votes[nearest(palette, Rgb{image.
data[at + 0], image.
data[at + 1], image.
data[at + 2]})]++;
87 std::vector<size_t> order;
91 std::vector<size_t> all(entries.size());
92 for (
size_t at = 0; at < all.size(); at++) all[at] = at;
96 std::ranges::stable_sort(all, [&](
size_t a,
size_t b) {
return votes[a] > votes[b]; });
97 order.assign(all.begin(), all.begin() + most);
99 std::vector<bool> taken(entries.size(),
false);
103 for (
size_t at = 1; at < votes.size(); at++) {
104 if (votes[at] > votes[first]) first = at;
106 order.push_back(first);
109 while (order.size() < most) {
110 size_t best = entries.size();
111 double bestScore = -1;
113 for (
size_t at = 0; at < entries.size(); at++) {
114 if (taken[at])
continue;
117 int apart = std::numeric_limits<int>::max();
118 for (
const auto chosen : order) {
119 apart = std::min(apart, distance(palette[at], palette[chosen]));
123 const auto score =
static_cast<double>(votes[at]) *
static_cast<double>(apart);
124 if (score > bestScore) {
131 if (best == entries.size() || bestScore <= 0)
break;
132 order.push_back(best);
138 std::ranges::sort(order);
140 std::vector<uint32_t> kept;
142 for (
const auto at : order) kept.push_back(entries[at]);
147 std::vector<uint32_t> entries,
Dither how) {
148 if (entries.empty()) {
151 if (entries.size() > 256) {
155 const auto width =
static_cast<size_t>(image.
width);
156 const auto height =
static_cast<size_t>(image.
height);
157 if (image.
data.size() != width * height * 4) {
161 std::vector<Rgb> palette;
162 palette.reserve(entries.size());
163 for (
const auto colour : entries) palette.push_back(unpack(colour));
165 std::vector<uint8_t> indices(width * height);
169 std::vector<Rgb> carried(how ==
Dither::Floyd ? width * height : 0);
173 const int spread = 256 /
static_cast<int>(std::max<size_t>(entries.size(), 2));
179 for (
size_t y = 0; y < height; y++) {
180 for (
size_t x = 0; x < width; x++) {
181 const auto at = (y * width + x) * 4;
182 Rgb wanted{image.
data[at + 0], image.
data[at + 1], image.
data[at + 2]};
187 const auto half = matrix * matrix / 2;
189 static_cast<int>(y % matrix)) - half;
190 wanted.r = clampByte(wanted.r + threshold * spread / half);
191 wanted.g = clampByte(wanted.g + threshold * spread / half);
192 wanted.b = clampByte(wanted.b + threshold * spread / half);
194 const auto& error = carried[y * width + x];
195 wanted.r = clampByte(wanted.r + error.r);
196 wanted.g = clampByte(wanted.g + error.g);
197 wanted.b = clampByte(wanted.b + error.b);
200 const auto chosen = nearest(palette, wanted);
202 if (how ==
Dither::Strict && distance(palette[chosen], wanted) != 0) {
204 "the colour at " + std::to_string(x) +
"," + std::to_string(y)
205 +
" is not in the palette, and -filter error means every one must be"});
207 indices[y * width + x] =
static_cast<uint8_t
>(chosen);
213 const auto& got = palette[chosen];
214 const Rgb left{wanted.r - got.r, wanted.g - got.g, wanted.b - got.b};
216 const auto spill = [&](
size_t nx,
size_t ny,
int part) {
217 if (nx >= width || ny >= height)
return;
218 auto& into = carried[ny * width + nx];
219 into.r += left.r * part / 16;
220 into.g += left.g * part / 16;
221 into.b += left.b * part / 16;
224 if (x > 0) spill(x - 1, y + 1, 3);
226 spill(x + 1, y + 1, 1);