Porytiles
Loading...
Searching...
No Matches
palette_matchers.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <set>
5
9
10namespace porytiles2 {
11
22template <SupportsTransparency ColorType>
27 bool is_covered = false;
28
32 std::set<ColorType> missing_colors;
33
37 std::set<ColorType> covered_colors;
38
47 std::vector<std::size_t> uncovered_pixel_indices;
48
52 unsigned int pal_index = 0;
53};
54
55namespace details {
56
78template <SupportsTransparency ColorType, typename TransparencyPredicate>
80 const PixelTile<ColorType> &tile, const Palette<ColorType> &palette, TransparencyPredicate is_transparent_pred)
81{
83
84 // Get the palette colors as a set for efficient lookup
85 const auto &palette_colors_vec = palette.colors();
86 std::set<ColorType> palette_colors_set{palette_colors_vec.begin(), palette_colors_vec.end()};
87
88 // Extract all unique non-transparent colors from the tile and track uncovered pixels
89 std::set<ColorType> tile_colors;
90 for (std::size_t i = 0; i < tile::size_pix; ++i) {
91 const auto &pixel = tile.at(i);
92 if (!is_transparent_pred(pixel)) {
93 tile_colors.insert(pixel);
94
95 // If this pixel's color is not in the palette, record its index
96 if (!palette_colors_set.contains(pixel)) {
97 result.uncovered_pixel_indices.push_back(i);
98 }
99 }
100 }
101
102 // Categorize each tile color as covered or missing
103 for (const auto &color : tile_colors) {
104 if (palette_colors_set.contains(color)) {
105 result.covered_colors.insert(color);
106 }
107 else {
108 result.missing_colors.insert(color);
109 }
110 }
111
112 // The tile is covered if there are no missing colors
113 result.is_covered = result.missing_colors.empty();
114
115 return result;
116}
117
118} // namespace details
119
141template <SupportsTransparency ColorType>
142[[nodiscard]] PaletteMatchResult<ColorType>
144 requires requires(const ColorType &c) { c.is_transparent(); }
145{
146 return details::match_tile_to_palette_impl(tile, palette, [](const ColorType &c) { return c.is_transparent(); });
147}
148
167template <SupportsTransparency ColorType>
168[[nodiscard]] PaletteMatchResult<ColorType>
169match_tile_to_palette(const PixelTile<ColorType> &tile, const Palette<ColorType> &palette, const ColorType &extrinsic)
170 requires requires(const ColorType &c) { c.is_transparent(c); }
171{
172 if (palette.size() == 0) {
173 panic("palette is empty");
174 }
175
176 if (palette.colors().at(0) != extrinsic) {
177 // TODO: we should have an earlier compilation step that normalizes transparency in Porymap pals, since
178 // their default slot 0 transparency doesn't matter. When you import a vanilla set to Porytiles, all
179 // transparent pixels get normalized to the configured extrinsic transparency. During this earlier step, we can
180 // warn the user that the slot 0 of their Porymap pal will be overwritten. We can explain in a note that this
181 // should not be an issue.
182 panic("palette slot 0 did not match provided extrinsic transparency value");
183 }
184
186 tile, palette, [&extrinsic](const ColorType &c) { return c.is_transparent(extrinsic); });
187}
188
219template <SupportsTransparency ColorType>
220[[nodiscard]] std::vector<PaletteMatchResult<ColorType>> match_or_best(
221 const PixelTile<ColorType> &tile,
222 const std::vector<Palette<ColorType>> &palettes,
223 const ColorType &extrinsic,
224 std::size_t top_n)
225 requires requires(const ColorType &c) { c.is_transparent(c); }
226{
227 if (palettes.empty()) {
228 panic("palettes vector is empty");
229 }
230 if (top_n == 0) {
231 panic("top_n must be greater than 0");
232 }
233
234 // Match tile against all palettes
235 std::vector<PaletteMatchResult<ColorType>> complete_matches;
236 std::vector<PaletteMatchResult<ColorType>> incomplete_matches;
237
238 for (std::size_t i = 0; i < palettes.size(); ++i) {
239 auto result = match_tile_to_palette(tile, palettes[i], extrinsic);
240 result.pal_index = static_cast<unsigned int>(i);
241
242 if (result.is_covered) {
243 complete_matches.push_back(result);
244 }
245 else {
246 incomplete_matches.push_back(result);
247 }
248 }
249
250 // If we found any complete matches, return all of them (ignore top_n)
251 if (!complete_matches.empty()) {
252 return complete_matches;
253 }
254
255 // No complete matches found, sort incomplete matches by quality (fewer missing_colors is better)
256 std::sort(incomplete_matches.begin(), incomplete_matches.end(), [](const auto &a, const auto &b) {
257 return a.missing_colors.size() < b.missing_colors.size();
258 });
259
260 // Return top_n results (or all if fewer than top_n)
261 if (incomplete_matches.size() > top_n) {
262 incomplete_matches.resize(top_n);
263 }
264
265 return incomplete_matches;
266}
267
268} // namespace porytiles2
A palette container for colors that support transparency checking.
Definition palette.hpp:34
const std::vector< ColorType > & colors() const
Returns a const reference to the internal color vector.
Definition palette.hpp:99
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 > &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 > &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.hpp:53
std::vector< PaletteMatchResult< ColorType > > match_or_best(const PixelTile< ColorType > &tile, const std::vector< Palette< ColorType > > &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.
unsigned int pal_index
The palette index of the match, useful in batch operations.
std::vector< std::size_t > uncovered_pixel_indices
The linear indices of tile pixels whose colors are not covered by the palette.
std::set< ColorType > covered_colors
The set of non-transparent colors from the tile that ARE present in the palette.
bool is_covered
True if the palette covers all non-transparent colors in the tile, false otherwise.