Porytiles
Loading...
Searching...
No Matches
tile_converters.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <map>
4
13
14namespace porytiles {
15
16namespace details {
17
34template <SupportsTransparency PixelType, typename TransparencyPredicate>
36 const PixelTile<PixelType> &pixel_tile,
37 const ColorIndexMap<PixelType> &color_index_map,
38 TransparencyPredicate is_transparent_pred)
39{
40 // Map from color index to ShapeMask
41 std::map<ColorIndex, ShapeMask> index_to_mask;
42
43 // Iterate through all pixels
44 for (std::size_t row = 0; row < tile::side_length_pix; ++row) {
45 for (std::size_t col = 0; col < tile::side_length_pix; ++col) {
46 const auto pixel = pixel_tile.at(row, col);
47
48 // Skip transparent pixels
49 if (is_transparent_pred(pixel)) {
50 continue;
51 }
52
53 // Look up the color index
54 auto index_opt = color_index_map.index_at_color(pixel);
55 if (!index_opt) {
56 panic("Pixel not found in ColorIndexMap");
57 }
58
59 const ColorIndex &index = *index_opt;
60
61 // Create mask if it doesn't exist
62 if (index_to_mask.find(index) == index_to_mask.end()) {
63 index_to_mask[index] = ShapeMask{};
64 }
65
66 // Set the bit for this position
67 index_to_mask[index].set(row, col);
68 }
69 }
70
71 // Build the result ShapeTile
73 for (const auto &[index, mask] : index_to_mask) {
74 result.set(mask, index);
75 }
76
77 return result;
78}
79
104template <SupportsTransparency ColorType, std::size_t N = 0, typename TransparencyPredicate>
106 const PixelTile<ColorType> &tile, const Palette<ColorType, N> &palette, TransparencyPredicate is_transparent_pred)
107{
108 // Build a color-to-index map for efficient lookup
109 // Note: palette.color_to_index_map() returns PaletteIndex, convert to std::size_t
110 std::map<ColorType, std::size_t> color_to_index;
111 for (const auto &[color, pal_idx] : palette.color_to_index_map()) {
112 color_to_index[color] = pal_idx.value();
113 }
114
115 // Convert each pixel
116 std::array<IndexPixel, tile::size_pix> index_pixels;
117
118 for (std::size_t i = 0; i < tile::size_pix; ++i) {
119 const auto &pixel = tile.at(i);
120
121 if (is_transparent_pred(pixel)) {
122 // Transparent pixels map to index 0
123 index_pixels[i] = IndexPixel{0};
124 }
125 else {
126 // Non-transparent pixel requires a non-empty palette
127 if (palette.size() == 0) {
128 panic("non-transparent pixel found but palette is empty");
129 }
130
131 // Look up the color in the palette
132 auto it = color_to_index.find(pixel);
133 if (it != color_to_index.end()) {
134 index_pixels[i] = IndexPixel{it->second};
135 }
136 else {
137 panic("color not found in palette");
138 }
139 }
140 }
141
142 return PixelTile{index_pixels};
143}
144
167template <SupportsTransparency ColorType, std::size_t N = 0>
169 const PixelTile<IndexPixel> &index_tile, const Palette<ColorType, N> &palette, const ColorType &transparent_color)
170{
171 if (palette.size() == 0) {
172 panic("palette is empty");
173 }
174
175 const auto index_to_color = palette.index_to_color_map();
176 std::array<ColorType, tile::size_pix> color_pixels;
177
178 for (std::size_t i = 0; i < tile::size_pix; ++i) {
179 const auto &index_pixel = index_tile.at(i);
180 // Use color_index() to extract the lower 4 bits, which is the actual palette color index.
181 // This is critical for true-color mode where the full 8-bit value encodes both palette index (upper 4 bits)
182 // and color index (lower 4 bits). For standard 4-bit pixels, color_index() == index().
183 const std::size_t color_index = index_pixel.color_index();
184
185 if (color_index == 0) {
186 // Color index 0 is the transparent slot
187 color_pixels[i] = transparent_color;
188 }
189 else {
190 // Look up in index-to-color map for non-zero indices
191 auto it = index_to_color.find(PaletteIndex{color_index});
192 if (it == index_to_color.end()) {
193 panic(
194 "color_index " + std::to_string(color_index) + " out of palette bounds [0, " +
195 std::to_string(palette.size()) + ")");
196 }
197 color_pixels[i] = it->second;
198 }
199 }
200
201 return PixelTile<ColorType>{color_pixels};
202}
203
204} // namespace details
205
230template <SupportsTransparency PixelType>
231[[nodiscard]] ShapeTile<ColorIndex>
232from_pixel_tile(const PixelTile<PixelType> &pixel_tile, const ColorIndexMap<PixelType> &color_index_map)
233 requires requires(const PixelType &p) { p.is_transparent(); }
234{
236 pixel_tile, color_index_map, [](const PixelType &p) { return p.is_transparent(); });
237}
238
265template <SupportsTransparency PixelType>
267 const PixelTile<PixelType> &pixel_tile, const ColorIndexMap<PixelType> &color_index_map, const PixelType &extrinsic)
268 requires requires(const PixelType &p) { p.is_transparent(p); }
269{
271 pixel_tile, color_index_map, [&extrinsic](const PixelType &p) { return p.is_transparent(extrinsic); });
272}
273
297template <SupportsTransparency PixelType>
298[[nodiscard]] PixelTile<PixelType>
299from_shape_tile(const ShapeTile<ColorIndex> &shape_tile, const ColorIndexMap<PixelType> &color_index_map)
300{
301 // Start with a default (transparent) PixelTile
303
304 // Track which pixels have been set to detect overlaps
305 std::array<bool, tile::size_pix> pixel_set{};
306
307 // Iterate through each (ShapeMask, ColorIndex) pair
308 for (const auto &[mask, index] : shape_tile.colors()) {
309 // Look up the actual color from the ColorIndexMap
310 auto color_opt = color_index_map.color_at_index(index);
311 if (!color_opt) {
312 panic("ColorIndex not found in ColorIndexMap");
313 }
314
315 const PixelType &color = *color_opt;
316
317 // Set all pixels marked by this ShapeMask
318 for (std::size_t row = 0; row < tile::side_length_pix; ++row) {
319 for (std::size_t col = 0; col < tile::side_length_pix; ++col) {
320 if (mask.get(row, col)) {
321 std::size_t pixel_index = row * tile::side_length_pix + col;
322
323 // Check for overlap
324 if (pixel_set[pixel_index]) {
325 panic("Overlapping masks detected in ShapeTile - programmer error");
326 }
327
328 result.set(row, col, color);
329 pixel_set[pixel_index] = true;
330 }
331 }
332 }
333 }
334
335 return result;
336}
337
360template <SupportsTransparency PixelType>
361[[nodiscard]] ShapeTile<PixelType>
363{
365
366 // Iterate through each (ShapeMask, ColorIndex) pair
367 for (const auto &[mask, index] : shape_tile.colors()) {
368 // Look up the actual color from the ColorIndexMap
369 auto color_opt = color_index_map.color_at_index(index);
370 if (!color_opt) {
371 panic("ColorIndex not found in ColorIndexMap");
372 }
373
374 const PixelType &color = *color_opt;
375
376 // Set the mask with the pixel color in the result
377 result.set(mask, color);
378 }
379
380 return result;
381}
382
400template <SupportsTransparency ColorType, std::size_t N = 0>
401[[nodiscard]] PixelTile<ColorType>
403 requires requires(const ColorType &c) { c.is_transparent(); }
404{
405 return details::color_tile_from_index_tile_impl(index_tile, palette, ColorType{});
406}
407
426template <SupportsTransparency ColorType, std::size_t N = 0>
428 const PixelTile<IndexPixel> &index_tile, const Palette<ColorType, N> &palette, const ColorType &extrinsic)
429 requires requires(const ColorType &c) { c.is_transparent(c); }
430{
431 return details::color_tile_from_index_tile_impl(index_tile, palette, extrinsic);
432}
433
450template <SupportsTransparency ColorType, std::size_t N = 0>
451[[nodiscard]] PixelTile<IndexPixel>
453 requires requires(const ColorType &c) { c.is_transparent(c); }
454{
456 tile, palette, [](const ColorType &c) { return c.is_transparent(); });
457}
458
476template <SupportsTransparency ColorType, std::size_t N = 0>
478 const PixelTile<ColorType> &tile, const Palette<ColorType, N> &palette, const ColorType &extrinsic)
479 requires requires(const ColorType &c) { c.is_transparent(c); }
480{
482 tile, palette, [&extrinsic](const ColorType &c) { return c.is_transparent(extrinsic); });
483}
484
485} // namespace porytiles
A bidirectional mapping between pixel color values and sequential integer indices.
std::optional< ColorIndex > index_at_color(const PixelType &color) const
Retrieves the index associated with a given color.
std::optional< PixelType > color_at_index(ColorIndex index) const
Retrieves the color associated with a given index.
Represents a color index value for palette operations.
Represents an indexed color pixel.
Represents a palette index value within a particular palette.
A generic palette container for colors that support transparency checking.
Definition palette.hpp:47
std::map< PaletteIndex, ColorType > index_to_color_map() const
Creates a map from palette indices to their colors.
Definition palette.hpp:346
std::size_t size() const
Returns the number of slots in the palette.
Definition palette.hpp:229
std::map< ColorType, PaletteIndex > color_to_index_map() const
Creates a map from colors to their palette indices.
Definition palette.hpp:325
An 8x8 tile backed by literal-array-based per-pixel storage of an arbitrary pixel type.
bool is_transparent() const
Checks if this entire PixelTile is transparent (intrinsic transparency only).
PixelType at(std::size_t i) const
void set(std::size_t i, const PixelType &p)
Represents which pixels in an 8x8 tile are non-transparent.
void set(std::size_t row, std::size_t col)
Sets the bit at the specified row and column to 1.
An 8x8 tile backed by mask-based storage that maps shape regions to pixel values.
void set(const ShapeMask &mask, const PixelType &color)
Sets or updates the pixel value for a specific shape mask.
bool is_transparent() const
Checks if this entire ShapeTile is transparent.
const std::map< ShapeMask, PixelType > & colors() const
Returns the internal map of shape masks to pixel values.
PixelTile< IndexPixel > index_tile_from_color_tile_impl(const PixelTile< ColorType > &tile, const Palette< ColorType, N > &palette, TransparencyPredicate is_transparent_pred)
Helper function implementing the core color-to-index tile conversion logic.
ShapeTile< ColorIndex > from_pixel_tile_impl(const PixelTile< PixelType > &pixel_tile, const ColorIndexMap< PixelType > &color_index_map, TransparencyPredicate is_transparent_pred)
Helper function implementing the core PixelTile to ShapeTile conversion logic.
PixelTile< ColorType > color_tile_from_index_tile_impl(const PixelTile< IndexPixel > &index_tile, const Palette< ColorType, N > &palette, const ColorType &transparent_color)
Helper function implementing the core index-to-color tile conversion logic.
constexpr std::size_t side_length_pix
constexpr std::size_t size_pix
void panic(const StringViewSourceLoc &s)
Unconditionally terminates the program with a panic message.
Definition panic.cpp:43
PixelTile< PixelType > from_shape_tile(const ShapeTile< ColorIndex > &shape_tile, const ColorIndexMap< PixelType > &color_index_map)
Converts a ShapeTile<ColorIndex> to a PixelTile using a ColorIndexMap.
PixelTile< IndexPixel > index_tile_from_color_tile(const PixelTile< ColorType > &tile, const Palette< ColorType, N > &palette)
Converts a PixelTile<ColorType> to indexed form using a palette (intrinsic transparency only).
ShapeTile< ColorIndex > from_pixel_tile(const PixelTile< PixelType > &pixel_tile, const ColorIndexMap< PixelType > &color_index_map)
Converts a PixelTile to a ShapeTile<ColorIndex> using a ColorIndexMap (intrinsic transparency).
ShapeTile< PixelType > shape_tile_to_pixel_colors(const ShapeTile< ColorIndex > &shape_tile, const ColorIndexMap< PixelType > &color_index_map)
Converts a ShapeTile<ColorIndex> to a ShapeTile<PixelType> using a ColorIndexMap.
PixelTile< ColorType > color_tile_from_index_tile(const PixelTile< IndexPixel > &index_tile, const Palette< ColorType, N > &palette)
Converts a PixelTile<IndexPixel> to a PixelTile<ColorType> using a palette (intrinsic transparency).