Porytiles
Loading...
Searching...
No Matches
palette_matchers.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <ranges>
5#include <set>
6
10
11namespace porytiles {
12
21template <SupportsTransparency ColorType>
24 bool is_covered = false;
25
27 std::set<ColorType> missing_colors;
28
30 std::set<ColorType> covered_colors;
31
38 std::vector<std::size_t> uncovered_pixel_indices;
39
41 std::size_t palette_index = 0;
42};
43
44namespace details {
45
66template <SupportsTransparency ColorType, typename TransparencyPredicate, std::size_t N = 0>
68 const PixelTile<ColorType> &tile, const Palette<ColorType, N> &palette, TransparencyPredicate is_transparent_pred)
69{
71
72 // Get the palette colors as a set for efficient lookup. We don't include the slot 0 color, since it's irrelevant.
73 std::set<ColorType> palette_colors_set;
74 for (const auto &color : palette.color_to_index_map() | std::views::keys) {
75 palette_colors_set.insert(color);
76 }
77
78 // Extract all unique non-transparent colors from the tile and track uncovered pixels
79 std::set<ColorType> tile_colors;
80 for (std::size_t i = 0; i < tile::size_pix; ++i) {
81 const auto &pixel = tile.at(i);
82 if (!is_transparent_pred(pixel)) {
83 tile_colors.insert(pixel);
84
85 // If this pixel's color is not in the palette, record its index
86 if (!palette_colors_set.contains(pixel)) {
87 result.uncovered_pixel_indices.push_back(i);
88 }
89 }
90 }
91
92 // Categorize each tile color as covered or missing
93 for (const auto &color : tile_colors) {
94 if (palette_colors_set.contains(color)) {
95 result.covered_colors.insert(color);
96 }
97 else {
98 result.missing_colors.insert(color);
99 }
100 }
101
102 // The tile is covered if there are no missing colors
103 result.is_covered = result.missing_colors.empty();
104
105 return result;
106}
107
108} // namespace details
109
131template <SupportsTransparency ColorType, std::size_t N = 0>
132[[nodiscard]] PaletteMatchResult<ColorType>
134 requires requires(const ColorType &c) { c.is_transparent(); }
135{
136 if (palette.size() == 0) {
137 panic("palette is empty");
138 }
139 return details::match_tile_to_palette_impl(tile, palette, [](const ColorType &c) { return c.is_transparent(); });
140}
141
159template <SupportsTransparency ColorType, std::size_t N = 0>
161 const PixelTile<ColorType> &tile, const Palette<ColorType, N> &palette, const ColorType &extrinsic)
162 requires requires(const ColorType &c) { c.is_transparent(c); }
163{
164 if (palette.size() == 0) {
165 panic("palette is empty");
166 }
168 tile, palette, [&extrinsic](const ColorType &c) { return c.is_transparent(extrinsic); });
169}
170
200template <SupportsTransparency ColorType, typename PaletteContainer>
201[[nodiscard]] std::vector<PaletteMatchResult<ColorType>> match_or_best(
202 const PixelTile<ColorType> &tile, const PaletteContainer &palettes, const ColorType &extrinsic, std::size_t top_n)
203 requires requires(const ColorType &c) { c.is_transparent(c); }
204{
205 if (palettes.empty()) {
206 panic("palettes container is empty");
207 }
208 if (top_n == 0) {
209 panic("top_n must be greater than 0");
210 }
211
212 // Match tile against all palettes
213 std::vector<PaletteMatchResult<ColorType>> complete_matches;
214 std::vector<PaletteMatchResult<ColorType>> incomplete_matches;
215
216 for (std::size_t i = 0; i < palettes.size(); ++i) {
217 auto result = match_tile_to_palette(tile, palettes[i], extrinsic);
218 result.palette_index = i;
219
220 if (result.is_covered) {
221 complete_matches.push_back(result);
222 }
223 else {
224 incomplete_matches.push_back(result);
225 }
226 }
227
228 // If we found any complete matches, return all of them (ignore top_n)
229 if (!complete_matches.empty()) {
230 return complete_matches;
231 }
232
233 // No complete matches found, sort incomplete matches by quality (fewer missing_colors is better)
234 std::sort(incomplete_matches.begin(), incomplete_matches.end(), [](const auto &a, const auto &b) {
235 return a.missing_colors.size() < b.missing_colors.size();
236 });
237
238 // Return top_n results (or all if fewer than top_n)
239 if (incomplete_matches.size() > top_n) {
240 incomplete_matches.resize(top_n);
241 }
242
243 return incomplete_matches;
244}
245
246} // namespace porytiles
A generic palette container for colors that support transparency checking.
Definition palette.hpp:45
std::map< ColorType, PaletteIndex > color_to_index_map() const
Creates a map from colors to their palette indices.
Definition palette.hpp:291
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
PixelType at(std::size_t i) const
PaletteMatchResult< ColorType > match_tile_to_palette_impl(const PixelTile< ColorType > &tile, const Palette< ColorType, N > &palette, TransparencyPredicate is_transparent_pred)
Helper function implementing the core palette matching logic.
constexpr std::size_t size_pix
PaletteMatchResult< ColorType > match_tile_to_palette(const PixelTile< ColorType > &tile, const Palette< ColorType, N > &palette)
Matches a PixelTile against a Palette (intrinsic transparency only).
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
std::vector< PaletteMatchResult< ColorType > > match_or_best(const PixelTile< ColorType > &tile, const PaletteContainer &palettes, const ColorType &extrinsic, std::size_t top_n)
Finds the best palette match(es) for a tile (extrinsic transparency).
Result type for palette matching operations.
std::set< ColorType > missing_colors
The set of non-transparent colors from the tile that are NOT present in the palette.
std::vector< std::size_t > uncovered_pixel_indices
The linear indices of tile pixels whose colors are not covered by the palette.
std::size_t palette_index
The palette index of the match, useful in batch operations.
bool is_covered
True if the palette covers all non-transparent colors in the tile, false otherwise.
std::set< ColorType > covered_colors
The set of non-transparent colors from the tile that ARE present in the palette.